Sunday, August 09, 2015

Generating Resource Designer Files During Build with Visual Studio

In Visual Studio files containing resources have the extension *.resx. As you edit this file VS will use a custom tool to generate a strongly typed *designer.cs file. However, since this is a generated file, you should not need to put it under version control. Consequentially you will need to generate the file. One option is to use the context menu and select "Run Custom Tool". This is cumbersome if you check out the code base and have many resource files. It would be great, if those designer files were created automatically when building the project.

Another scenario is where resource files are re-used across multiple projects. Here, too, automatic generation during build would be useful.

Finally it would be great if a solution for automatically generating the designer files would work for both building the project in Visual Studio and building it via msbuild at the command line.

The solution presented here was developed with Visual Studio 2013 and C#. It may or may not work in other environments.

To automatically generate the *.designer.cs files from *.resx files, follow these steps:

1. Close your solution

2. Open as an XML file the project file in which you want to automatically generate the designer files. Note that you need to load it as an XML file. You can't edit these settings through the project property page.

3. Add a target to the project as follows:

<Target Name="GenerateDesignerFiles">
   <Message Text="Deleting old Designer Files..."/>
   <Delete Files="@(EmbeddedResource->'%(RootDir)%(Directory)%(Filename).resources')"/>
   <Delete Files="@(EmbeddedResource->'%(RootDir)%(Directory)%(Filename).designer.cs')"/>
   <Message Text="Generating Designer Files..."/>
   <GenerateResource
      Sources="@(EmbeddedResource)"
      StronglyTypedLanguage="C#"
      StronglyTypedClassName="%(Filename)"
      StronglyTypedNamespace="@(EmbeddedResource->'%(CustomToolNamespace)')"
      StronglyTypedFileName="@(EmbeddedResource->'%(RootDir)%(Directory)%(Filename).designer.cs')"
      PublicClass="true"
      >
   </GenerateResource>
   <Message Text="Generating Designer Files complete."/>
</Target>

4. Locate the target named "BeforeBuild". This target may be commented out (the default).

5. Modify the "BeforeBuild" target as follows:

<Target Name="BeforeBuild">
   <CallTarget Targets="GenerateDesignerFiles"/>
</Target>

This solution is based on all resource files being listed as "EmbeddedResource" within an ItemGroup of the project file, e.g.

<ItemGroup>
  <EmbeddedResource Include="Resources\Creditor\Display_Creditor.resx">
    <Generator>PublicResXFileCodeGenerator</Generator>
    <LastGenOutput>Display_Creditor.Designer.cs</LastGenOutput>
    <CustomToolNamespace>Acme.Web.Resources.Creditor</CustomToolNamespace>
  </EmbeddedResource>
  <EmbeddedResource Include="Resources\InboundEmail\Tooltip_InboundEmailDetails.resx">
    <Generator>PublicResXFileCodeGenerator</Generator>
    <LastGenOutput>Tooltip_InboundEmailDetails.Designer.cs</LastGenOutput>
    <CustomToolNamespace>Acme.Web.Resources.InboundEmail</CustomToolNamespace>
  </EmbeddedResource>
  <EmbeddedResource Include="Resources\Creditor\Tooltip_CreditorDetails.resx">
    <Generator>PublicResXFileCodeGenerator</Generator>
    <LastGenOutput>Tooltip_CreditorDetails.Designer.cs</LastGenOutput>
    <CustomToolNamespace>Acme.Web.Resources.Creditor</CustomToolNamespace>
  </EmbeddedResource>
</ItemGroup>

Disclaimer: This has been tested with Visual Studio 2013 and C# projects. It also works using msbuild. It may or may not work for other environments.