Welcome to our Bike Club!
Regards,
The AdventureWorks Team
<#= Member.DateJoined.ToShortDateString() #>
<#+ public ClubMember Member { get; set; } #>
Code snippet WelcomeLetter.tt
Vb
<#@ template language="VB" #>
Dear <#=Member.Salutation#> <#=Member.Surname#>,
Welcome to our Bike Club!
Regards,
The AdventureWorks Team
<#= Member.DateJoined.ToShortDateString() #>
<#+ Public Member as ClubMember #>
Code snippet WelcomeLetter.tt
This file generates a class called WelcomeLetter and relies on the following simple data class, which
is passed into the template via its Member property.
c#
public class ClubMember
{
public string Salutation { get; set; }
public string Surname { get; set; }
public DateTime DateJoined { get; set; }
}
Code snippet ClubMember.tt
Vb
Public Class ClubMember
Public Surname As String
Preprocessed Text Templates .
287
Public Salutation As String
Public DateJoined As Date
End Class
Code snippet ClubMember.tt
Finally, to create the letter you instantiate a WelcomeLetter object, set the Member property to a
ClubMember object, and call TransformText().
c#
// ...
var member = new ClubMember
{
Surname = "Fry",
Salutation = "Mr",
DateJoined = DateTime.Today
};
var letterGenerator = new WelcomeLetter();
letterGenerator.Member = member;
var letter = letterGenerator.TransformText();
// ...
Code snippet Program.cs
Vb
' ...
Dim NewMember As New ClubMember
With NewMember
.Surname = "Fry"
.Salutation = "Mr"
.DateJoined = Date.Today
End With
Dim LetterGenerator As New WelcomeLetter
LetterGenerator.Member = NewMember
Dim Letter = LetterGenerator.TransformText()
' ...
Code snippet Module1.vb
This can look a little awkward but WelcomeLetter is a partial class so you can change the API to be
whatever you want. Often you will find yourself making the constructor of the generator private and
create a few static methods to handle the creation and use of generator instances.
c#
public partial class WelcomeLetter
{
private WelcomeLetter() { }
continues
288 .
chaPter 14 code generATion WiTh T4
(continued)
public static string Create(ClubMember member)
{
return new WelcomeLetter { Member = member }.TransformText();
}
}
Code snippet WelcomeLetter.Extensions.cs
Vb
Namespace My.Templates
Partial Public Class WelcomeLetter
Private Sub New()
End Sub
Public Shared Function Create(ByVal Member As ClubMember) As String
Dim LetterGenerator As New WelcomeLetter()
LetterGenerator.Member = Member
Return LetterGenerator.TransformText()
End Function
End Class
End Namespace
Code snippet WelcomeLetter.Extensions.vb
The generator contains a StringBuilder, which it uses internally to build up the
input when TransformText is executed. This StringBuilder is not cleared out
when you run the TransformText method, which means that each time you run
it the results are appended to the results of the previous execution. This is why
the Create method presented creates a new WelcomeLetter object each time
instead of keeping one in a static (Shared) variable and re-using it.
differences between a standard t4 template
Aside from which aspect of the generation process is included in your project, a few other key
differences exist between a Preprocessed Text Template and a standard T4 template. First,
Preprocessed Text Templates are completely standalone classes. They do not inherit from a base
class by default and therefore do not rely on Visual Studio to execute. The TransformText()
method of the generator class does not run within a try/catch block so you will need to watch for
and handle errors yourself when executing the generator.
Not all T4 directives will make sense in a Preprocessed Text Template, and for those that do some
attributes will no longer make much sense. Here is a quick summary.
The template directive is still used but not all of the attributes make sense. The culture and language
attributes are fully supported. The language attribute must match that of the containing language or the
generator class cannot be compiled. The debug attribute is ignored because you can control the debug
status of the generator class by setting the project configuration as you would with any other class.
Tips and Tricks .
289
The inherits attribute is supported and has a significant impact on the generated class. If you
do not specify a base class, the generated file will be completely standalone and will contain
implementations of all of the helper functions such as Write and Error. If you do specify a base
class, it is up to the base class to specify these implementations and the generated class will rely on
those implementations to perform the generation work.
The hostspecific attribute is supported and generates a Host property on the generator class. This
property is of the Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost
type, which resides in the Microsoft.VisualStudio.TextTemplating assembly. It is up to you to
add a reference to this assembly to your project and to provide a member of the appropriate type
before calling the TransformText
method.
The import directive works as normal. The referenced namespaces are included in the generator
code file with using statements in C# and Import statements in VB. The include directive is also
fully supported.
The output and assembly directives are ignored. To add
an assembly to the template you simply add a reference
to the project as normal. The output filename is selected
based on the template filename and the selected language.
Finally, you can set the namespace of the generator class
in the Properties window of the template file shown in
Figure 14-6. The namespace is normally based on the
project defaults and the location of the template file
within the folder structure of the project.
tiPs and tricks
Following are a few things that might help you to take full advantage of T4:
.
Write the code you intend to generate first for one specific case as a normal C# or VB code
file. Once you are satisfied that everything is working as intended, copy the entire code file
into a .tt
file. Now start slowly making the code less specific and more generic by introducing
Statement blocks and Expression blocks, factoring out Class Feature blocks as you go.
.
Save frequently as you make changes. As soon as a change breaks the generated code or the
generator, simply reverse it and try again.
.
Never make changes directly to a generated file. The next time the template is saved those
changes will be lost.
.
Make generated classes partial. This makes the generated classes extensible, allowing you to
keep some parts of the class intact and regenerate the other parts. In fact this is one of the
reasons that the partial class functionality exists.
.
Use an extension that includes the word generated such as .generated.cs and
.generated.vb. This is a convention used by Visual Studio itself and will discourage other
users from making changes to template files.
fiGure 14-6
290
.
chaPter 14 code generATion WiTh T4
.
Similarly, include a comment toward the top of the generated file stating that the file is
generated along with instructions for how to change the contents and regenerate the file.
.
Make T4 template execution a part of your build process. This ensures that the content of
the generated files doesn’t get stale with respect to the metadata used to generate it.
.
If you don’t have a lot of things dependent upon the generated code produced by a normal
T4 Text Template, switch the custom tool over to make the template a Preprocessed
Template while you develop it. This brings the code generator into your project and allows
you to write unit tests against it.
.
Don’t use T4 to generate .tt files. If you are trying to use a code generator to generate
template files, the level of complexity when things go wrong increases substantially. At this
point it might be wise to consider a different strategy for your project.
.
Finally, an absolutely invaluable resource for anyone getting started with T4 is
http://www.olegsych.com. Oleg is a Visual C# MVP who maintains a blog with a
very large collection of articles about T4.
suMMary
Code generation can be a fantastic productivity gain for your projects and Visual Studio 2010 includes
some powerful tools for managing the process out of the box. In this chapter you have seen how to
create and use T4 templates to speed up common and generic coding tasks. Learning when and how to
apply T4 to your projects increases your productivity and makes your solutions far more flexible.
15
Project and item Templates
what’s in this chaPter?
.
Creating your own item templates
.
Creating your own project templates
.
Adding a wizard to your project templates
Most development teams build a set of standards that specify how they build applications.
This means that every time you start a new project or add an item to an existing project, you
have to go through a process to ensure that it conforms to the standard. Visual Studio 2010
enables you to create templates that can be reused without having to modify the standard
item templates that ship with Visual Studio 2010. This chapter describes how you can create
simple templates and then extend them with a wizard that can change how the project is
generated using the IWizard
interface.
creatinG teMPlates
Two types of templates exist: those that create new project items and those that create entire
projects. Both types of templates essentially have the same structure, as you see later, except
that they are placed in different template folders. The project templates appear in the New
Project dialog, whereas the item templates appear in the Add New Item dialog.
item template
Although it is possible to build a project item template manually, it is much quicker to
create one from an existing project item and make changes as required. This section begins
by looking at an item template — in this case an About form that contains some basic
information, such as the application’s version number and who wrote it.
292 .
chaPter 15 projecT And iTem TemplATeS
To begin, create a new Windows Forms
application (using your language of choice) called
StarterProject. Instead of creating an About form
from scratch, you can customize the About Box
template that ships with Visual Studio. Right-
click the StarterProject project, select Add .
New Item, and add a new About Box. Customize
the default About form by deleting the logo and
first column of the TableLayoutPanel control
(by selecting the table layout panel, going to
the Properties window, selecting the Columns
property, clicking its ellipsis button (…), and
deleting column 1). The customized About form
is shown in Figure 15-1.
To make a template out of the About form,
select the Export Template item from the File menu. This starts the Export Template Wizard,
shown in Figure 15-2. If you have unsaved changes in your solution, you will be prompted to save
before continuing. The first step is to determine what type of template you want to create. In this
case, select the Item Template radio button and make sure that the project in which the About form
resides is selected in the drop-down list.
fiGure 15-1
fiGure 15-2
Creating Templates .
293
Click Next. You will be prompted to select the item on which you want to base the template.
In this case, select the About form. The use of checkboxes is slightly misleading, because with item
templates you can select only a single item on which to base the template (selecting a second
item will deselect the item already selected). After you make your selection and click Next, the
dialog shown in Figure 15-3 enables you to include any assembly references that you may require.
This list is based on the list of references in the project in which that item resides. Because this is a
form, include a reference to the System.Windows.Forms library, which will be added to a project
when adding a new item of this type (if it has not already been added). Otherwise it is possible that
the project won’t compile if it did not have a reference to this assembly (Class Library projects don’t
generally reference this assembly by default).
fiGure 15-3
After selecting an assembly, a warning may be displayed underneath the list
stating that the selected assembly isn’t preinstalled with Visual Studio and may
prevent a user from using your template if the assembly isn’t available on their
machine. Be aware of this issue, and only select assemblies that your item really
needs.
The final step in the Export Template Wizard is to specify some properties of the template to be
generated, such as the name, description, and icon that will appear in the Add New Item dialog.
294 .
chaPter 15 projecT And iTem TemplATeS
Figure 15-4 shows the final dialog in the wizard. As you can see, there are two checkboxes, one for
displaying the output folder upon completion and one for automatically importing the new template
into Visual Studio 2010.
fiGure 15-4
By default, exported templates are created in the My Exported
Templates folder under the current user’s Documents\Visual
Studio 2010 folder. Inside this root folder are a number of folders
that contain user settings about Visual Studio 2010 (as shown in
Figure 15-5).
You will also notice the Templates folder in Figure 15-5. Visual Studio
2010 looks in this folder for additional templates to display when you