Item templates and custom resources
September 11, 2014 Leave a comment
In a previous post I wrote about Custom resources with T4 templates. Back then I used the ReSharper multi-file templates to add the .resx
and .tt
files to my projects. The one downside of that approach: it requires R# 8.x which might not be readily available.
Thus I wanted to find out how to create a Visual Studio item template that achieves the same goal. Microsoft has a step-by-step guide that explains the process. That gives you the basics. But there is some fine-tuning that they don’t explain.
If you add a .resx
file to your project the generated .Designer.cs
file is hidden in the solution explorer. I wanted to do the same and hide the .tt
file underneath the .resx
file. Preferably I wanted both the .tt
and the generated .Designer.cs
file on the level directly below the .resx
file (as shown in the first screenshot). But the TextTemplatingFileGenerator
always puts the generated output file on the level below the .tt
file (as in the second screenshot) on every run. I decided to stop battling VS. Its not worth the effort in this case.

Screenshot 1: Nice to have

Screenshot 2: What you actually get
To actually hide the .tt
file via the item template is quite easy if not really prominently advertised. There’s a post on stackoverflow that explains how you need to modify your template.
The second <ProjectItem>
is the interesting part. You need to prefix the .tt
file with the path to the .resx
file. When the item template is invoked that will translate to a <DependentUpon>
clause in your project file.
<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item"> <TemplateData> <DefaultName>Resource.resx</DefaultName> <Name>Customizable resources</Name> <Description>Creates a .resx file that uses a T4 template to generate strongly typed resources.</Description> <ProjectType>CSharp</ProjectType> <SortOrder>10</SortOrder> <Icon>__TemplateIcon.ico</Icon> </TemplateData> <TemplateContent> <References> <Reference> <Assembly>System</Assembly> </Reference> <Reference> <Assembly>mscorlib</Assembly> </Reference> </References> <ProjectItem SubType="" TargetFileName="$fileinputname$.resx" ReplaceParameters="true">Resource.resx</ProjectItem> <ProjectItem SubType="" TargetFileName="$fileinputname$.resx\$fileinputname$.tt" ReplaceParameters="true">Resource.tt</ProjectItem> </TemplateContent> </VSTemplate>
File 1: MyTemplate.vstemplate
Also make sure that you set ReplaceParameters="true"
for the .resx
file. You will want to add template parameters for the namespace and the class name.
For that I used two of the predefined parameters: $rootnamespace$
and $safeitemname$
. Note that the first one gives you the full path to your .resx
file and not the root namespace of the current project! If you place the resources in project Foo
in the folder Assets
the value of $rootnamespace$
will thus be Foo.Assets
. Maybe someone at MS thought that’s a funny way to lead developers on a wild goose chase …
And that’s what the .tt
file looks like
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30319.34003 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ <#@ template hostspecific="true" language="C#" #> <#@ output extension=".Designer.cs" #> <#@ assembly name="System.Xml" #> <#@ assembly name="System.Xml.Linq" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Xml.Linq" #> namespace $rootnamespace$ { using System; using System.ComponentModel; using System.Globalization; using System.Resources; using System.Threading; using MyI18n; public class $safeitemname$ { private static IResourceManager resourceManager; private static CultureInfo resourceCulture; [EditorBrowsableAttribute(EditorBrowsableState.Advanced)] public static IResourceManager ResourceManager { get { if(resourceManager == null) { IResourceManager temp = new ResourceManagerWrapper(new ResourceManager("$rootnamespace$.$safeitemname$", typeof($safeitemname$).Assembly)); resourceManager = temp; } return resourceManager; } set { resourceManager = value; } } [EditorBrowsableAttribute(EditorBrowsableState.Advanced)] public static CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } <# string resxFileName = this.Host.TemplateFile.Replace(".tt", ".resx"); XDocument doc = XDocument.Load(resxFileName); if(doc != null && doc.Root != null) { foreach(XElement x in doc.Root.Descendants("data")) { string name = x.Attribute("name").Value; WriteLine(string.Empty); WriteLine(" public static string " + name); WriteLine(" {"); WriteLine(" get { return $safeitemname$.ResourceManager.GetString(\"" + name + "\", resourceCulture ?? CultureInfo.CurrentUICulture); }"); WriteLine(" }"); } } #> } }
File 2: Resource.tt
Don’t forget to adjust your using
statements to the location of the IResourceManager
interface and the ResourceManagerWrapper
class.
Your .zip
file should look something like this:
Now copy it to "C:\Users\[YOUR USERNAME]\Documents\Visual Studio [YOUR VISUAL STUDIO VERSION]\Templates\ItemTemplates\Visual C#"
and fire up VS. Once you click “Add new item” your new template should appear right on top of the “Visual C# Items”.
You can download the template from my pet project’s site.