Visual Studio 2005

Create Reusable Project And Item Templates For Your Development Team

Matt Milner

This article discusses:

  • Consuming existing templates
  • Creating new project and item templates
  • Customizing templates through XML metadata
  • Extending the new project wizard
This article uses the following technologies:
Visual Studio 2005, XML

Code download available at:CodeTemplates.exe(139 KB)

Contents

Consuming an Existing Template
Creating Templates
Customizing Templates
Templates for Multiple Projects
Providing Guidance
Parameter Replacement
Extending the Wizard
Conclusion

Over my years of working with clients on building Microsoft® .NET Framework-based applications, I have often heard a common feature request for Visual Studio® .NET: "It would be great if we could create our own project types and item types so that all developers in our organization would have access to them." Many orgs want to create shared templates for projects such as Web sites or for project items, such as a default Web page or forms. Visual Studio 2005 introduces a new model for defining templates and starter kits for projects and items that make this not only possible, but relatively simple. I'll take a look at how to consume, create, and customize these templates.

In the past, users have been able to create project or item templates, but it took a lot of inside knowledge of arcane text file formats, JavaScript files, and folder naming conventions, as well as a little bit of magic, to make it work. With the new model, a user can create a simple ZIP file of the items to be included in the template and an XML metadata file that describes the template and its contents. This new model supports creating item templates such as a Web page, a C# or Visual Basic® code file, or a configuration file. It also supports single-project templates like Web sites, class libraries, or smart client applications, as well as multiproject solutions. In fact, most of the built-in project and item templates that you use to create new solutions in Visual Studio 2005 are based on this same template mechanism.

Consuming an Existing Template

The easiest way to understand how these templates work is to first take a look at how to consume a template that has already been created. In Visual Studio 2005, you can configure the locations to search for templates, and these locations can use any Universal Naming Convention (UNC) path. In the Options dialog box, under the Projects and Solutions settings node, you can configure the user-specific locations to look for project or item templates (see Figure 1). The default values for these settings point to template folders in your My Documents folder. The templates included with Visual Studio 2005 are located under the Visual Studio installation directory and are included in the project setup dialog boxes by default.

Figure 1 Template Location Options

Figure 1** Template Location Options **

By dropping a ZIP file with the appropriate metadata file in the user-configured template location, the new project or item template will appear in the respective dialog boxes. The sample code included with this article includes sample project item and project templates that can be copied into these directories for testing purposes. After adding the ZIP file, start Visual Studio 2005, choose File | New | Project, and notice that the new project template is included under the My Templates section of the dialog box (see Figure 2). Selecting this project template will create a new project with the items included in the ZIP file.

Figure 2 Your Templates Appear in the New Project Dialog Box

Figure 2** Your Templates Appear in the New Project Dialog Box **

There are several benefits gained by being able to consume templates in this way. First, because the user locations are configurable, development groups can define a file share on a common server where templates are deployed for the group. All developers in the group can configure their environment to look to that file share for templates. When a new template is deployed to the server by copying the ZIP file to the share, all developers immediately have access to it from within Visual Studio. After the initial deployment, changes to the template can be made in one place instead of having to be deployed to each developer's workstation.

In addition, because the template is used as a true template, it remains unchanged and can be used repeatedly to create new projects. This is a vast improvement over the old "copy and paste" approach where one developer would create a file or project and hand it off to another. Without careful management, the original was quickly lost and modified with no chance to get back to a known good configuration. Because the project and item template locations are configured independently, an organization can choose to have only project templates centralized and allow developers to create their own item templates.

In addition to templates, Visual Studio 2005 includes a starter kit concept. Starter kits enable broad consumption of samples through community sites from which templates can be downloaded. From the New Project dialog box, one of the options is Search Online Templates. Choosing this option allows you to search for starter kits online by keyword. When a starter kit is found, you can choose to download and install it into the appropriate directory so that you can then use it as a template for adding items or projects. As the name implies, starter kits are generally used as learning tools or samples to help convey knowledge or provide a starting point for larger projects.

Creating Templates

Consuming templates is a great start, but most developers will want to know how to create their own templates. Visual Studio 2005 makes it extremely easy to create your own templates right in the IDE. Once they are created, you have the ability to extend and change them.

The simplest type of template to create is an item template. To do so, simply open a project that includes the file you want to use as a template and then chooses File | Export Template to run the Export Template Wizard shown in Figure 3.

Figure 3 Exporting an Item Template

Figure 3** Exporting an Item Template **

The wizard will prompt you to indicate the item to export and any assembly references to include. Adding references allows you to ensure that assemblies your template depends on are included in the project when the item is added. For example, if an item template uses the System.Data.SqlXml assembly to access SQL Server™ XML functionality, it is important to make sure this assembly gets referenced by the project when the template is added. The references available to choose from in the wizard include the default assembly references for the current project type as well as any references that have been added to the current project before running the wizard.

In addition to the assembly references, the export wizard is intelligent enough to export resource files required by the item be exported. For example, if an ASP.NET Web page is the item being exported, the C# or Visual Basic codebehind file and resource file for that page will also get exported and included when the template is used to create new items. The wizard also edits these files to add placeholders for class names and namespaces so that when they are added to a new project they can be named differently and show up in the correct code namespace. For example, instead of having a class that's always named BusinessObjectClass when you add your template, the user can specify a name for the new code file, and this will be used as the name for the class in your template.

When the parameters have been selected for the item template—including a name, a description, and the icon to use when displaying the template—you have the option of installing the template. When exporting the template, it gets created in your My Documents\Visual Studio 2005\My Exported Templates folder. Templates in this folder are not included in the New Item or New Project dialog boxes by default. In order to make the template available for use, it must be installed by copying it to the configured directory for templates. Checking the box to install the template simply means that the ZIP file will get copied to the folder you configured for templates in addition to the exported templates folder.

Not installing the template allows you to edit the metadata directly or add other files to the ZIP file before installing the template into the appropriate folder. For example, if you created a Master Page template for use in Web sites, you might want to include a CSS file that includes the styles used on the Master Page to ensure that the styles used are also included when the Master Page template is selected. In this case, you would not want to install the template at this point until you have had a chance to add the stylesheet file to the ZIP file and update the metadata file to include the stylesheet when installing the template. I will look at the steps required to customize a template like this later in the article.

Creating a project template works in much the same way with the exception that assembly references are not needed as they are inferred from the current project configuration. The main difference between the project template and an item template is that a project template provides the means to create a new project, whereas the item template can only be added to an existing project. Both template types, however, allow for multiple items to be added to a project when the wizard is run.

Customizing Templates

As you can see, creating a project or item template has been made extremely simple in Visual Studio 2005. However, like many wizards, the default template created may not be exactly what you want. Fortunately, because the design of the templates involves a ZIP file and a metadata file, it is not difficult to extend a template with new features once it's created.

The template metadata file is an XML file that describes the template and indicates the files and resources to be included in with the project or item. A sample metadata file created by using the wizard to export an item template is shown in Figure 4.

Figure 4 Metadata File for an Item Template

<VSTemplate Version="2.0.0" xmlns="https://schemas.microsoft.com/developer/vstemplate/2005" Type="Item"> <TemplateData> <Icon>__TemplateIcon.ico</Icon> <DefaultName>MasterPage.master</DefaultName> <Name>Example Org Master Page</Name> <Description> The default master page for Example Org web sites </Description> <ProjectType>Web</ProjectType> <ProjectSubType>CSharp</ProjectSubType> </TemplateData> <TemplateContent> <References> <Reference> <Assembly>System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089 </Assembly> </Reference> <Reference> <Assembly>mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089 </Assembly> </Reference> </References> <ProjectItem TargetFileName="$fileinputname$.master" ReplaceParameters="true">master~1.mas</ProjectItem> <ProjectItem TargetFileName="$fileinputname$.master.cs" ReplaceParameters="true">master~1.cs</ProjectItem> </TemplateContent> </VSTemplate>

The first thing to notice is the general structure of this metadata, which includes a root VSTemplate node and two child nodes: TemplateData and TemplateContent. The VSTemplate node simply acts as a wrapper for the other two items and indicates the type of the template. Because of the XML namespace declaration in the file, any developer editing this file in Visual Studio 2005 will automatically get IntelliSense® support for the items that are allowed, based on the schema for template files.

The TemplateData element contains information that is used to provide details about the template in the New Project or New Item dialog box. The name, description, and icon elements are all displayed directly in the dialog box as you would expect. The DefaultName element is used as the root of the default name provided for the new item or project. The ProjectType element indicates the type of project that the item can be included in and affects the location in the dialog box. The example shown in Figure 4 will cause this item template to be available only for C# Web projects.

Figure 5 shows some other optional elements that can be used in the TemplateData section of the metadata file, as well as options for the items already mentioned.

Figure 5 TemplateData Elements for an Item

Element Name Description
ProjectType Indicates the type of project that this item can be added to. Possible values include CSharp, VisualBasic, VisualC, JSharp, and Web.
Supports­MasterPage For items being added to Web projects only, this Boolean option allows you to indicate that the user should be able to select a master page to go along with your item. This is useful for custom Web page item templates that you create.
Supports­CodeSeparation For items being added to Web projects only, this Boolean option allows you to indicate that the user should be able to choose if they want their code separated from their presentation. Again, this is most useful for custom Web page item templates that you might create.
Supports­LanguageDropdown If a template is identical for multiple languages, providing a value of true in this element will allow the user to choose a language for the template. This is only used in Web projects where the template may come from different languages such as the WebForm template. When the language is changed, the metadata file from the new template location is used and the dialog updated. For example, if you switch the language from C# to Visual J# when adding a Web form, the code separation option is disabled because the Visual J# template does not support code separation.

The TemplateContent element is where you define the files and references for your item template. The TemplateContent element references any assemblies to be included with the item or project and generally will not need to be edited since you can specify these in the export wizard.

Each item in the ZIP file that is to be included in the project is called out with a ProjectItem element. In addition to identifying the source files, the attributes of the ProjectItem element allow you to indicate whether an item has a subtype, whether parameters in the file should be replaced, and whether the target file name differs from the source file name. The subtype attribute can include values such as Form or Component, which provide Visual Studio with hints about how to display the item in the correct designer when it is opened.

Replacing parameters allows the New Item wizard to create namespaces, class names, and similar items dynamically so that the template is not entirely static. Without specifying true for this attribute, the template files will appear in the new project exactly as they are in the ZIP file.

The TargetFileName attribute allows you to specify either a constant or parameterized value for the file name as it should appear in the new project. This allows you to reuse a template file to create multiple instances of the item in the same project. For example, a class file could be added to the same project twice with different TargetFileName attributes. When the item is added, two new classes will be added to the project based on the same template file. Allowing for this type of reuse within the template makes it much easier to manage the files involved in creating a template.

When creating project templates, the process is very similar to that used for item templates, but there are a few slight differences in the metadata file. First, because this is a project, all ProjectItem elements are included under a Project element, which indicates the project file name and whether parameters should be replaced when naming the project file. An abbreviated project template file is shown in Figure 6.

Figure 6 Metadata for a Project Template

<VSTemplate Version="2.0.0" xmlns="https://schemas.microsoft.com/developer/vstemplate/2005" Type="Project"> <TemplateData> ... </TemplateData> <TemplateContent> <References> ... </References> <Project TargetFileName="WinFormTemplate.csproj" File="WinFor~1.csp" ReplaceParameters="true"> <ProjectItem SubType="Code" TargetFileName="$fileinputname$.cs" ReplaceParameters="true">Class1.cs</ProjectItem> <Folder Name="OtherClasses"> <ProjectItem SubType="Code">Class2.cs</ProjectItem> </Folder> </Project> </TemplateContent> </VSTemplate>

Notice that in addition to the Project element that identifies the project information, this example also uses the Folder element to indicate that the ZIP file contains a folder containing the project items indicated. The folder will be included in the project structure when the template is used. The project template also provides different options for the TemplateData section to influence the user interaction with the New Project dialog box. These items are shown in Figure 7.

Figure 7 TemplateData Elements for a Project

Element Name Description
LocationField Options include Hidden, Disabled, or Enabled. This allows you to indicate whether the user should be able to specify a location for their new project. Defaults to Enabled.
EnableLocationBrowseButton Indicates if the user should be able to browse the file system for a location to choose for the new project. Defaults to True.
CreateNewFolder Indicates that the wizard should create a new folder for the project at the location selected. Defaults to True.
AllowCreationWithoutSave Indicates if this project template can be an in-memory temporary project. This element provides support for a new feature in Visual Studio 2005 which allows a user to create a new project and work on it, but if the project is not saved, it is discarded. This allows for quick testing or prototyping without having to fill up the My Projects folder with a lot of projects that will never get used.
ProjectSubType Used by internal templates to further restrict the location within the dialog to certain project subtypes such as Windows, Office, or Database. Used by Web projects to specify the language type.

Web projects are configured in a slightly different manner as they use a different wizard for project creation. Instead of using a language for the project type such as Visual Basic and then a ProjectSubType such as Windows, a Web project uses Web as the project type, and the language as the ProjectSubType.

Templates for Multiple Projects

In addition to being able to configure a template for a single project, it is often useful to create an entire solution as a template or starting point. For example, a development group might not simply want a single Web project as the starting point for a new Web solution as they may want to include business object and data access projects. One resolution would be to create a template for each project and allow the developer to add each project they needed individually. However, a much simpler approach, and one that provides more consistency, is to create a template for the entire solution and give a developer all of the projects they need to get started.

Multi-project templates still involve only one ZIP file, but they include one master metadata file and an individual metadata file for each project in the solution. The project metadata files are no different than what has been discussed so far, but the solution template file now provides the template data to the wizard through the TemplateData element and its children and provides pointers to the other project metadata files through the TemplateContents element and its children. The abbreviated sample in Figure 8 shows a solution metadata file that includes three projects: a Web site, a business logic layer, and a data access layer.

Figure 8 Metadata for a Solution

<VSTemplate Version="2.0.0" xmlns="https://schemas.microsoft.com/developer/vstemplate/2005" Type="ProjectGroup"> <TemplateData> ... </TemplateData> <TemplateContent> <ProjectCollection> <ProjectTemplateLink Projectname="DataAccessLayer"> .\DAL\DAL.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="BusinessLogic"> .\BAL\BAL.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="WebSite"> .\WebSite\WebSite.vstemplate </ProjectTemplateLink> </ProjectCollection> </TemplateContent> </VSTemplate>

Figure 9 Solution Folder

Figure 9** Solution Folder **

Notice that the Type for the template is a ProjectGroup and that the template content is simply a collection of links to the other project templates. A friendly name can be given to the projects, and this is how they will be named in the Solution Explorer. The paths to the other projects are based on the folder structure of the items in the ZIP file. The ZIP file used with the example template metadata in Figure 8 would look something like Figure 9. Each project in the solution has its own folder and its own vstemplate metadata file, and the links in the example provide relative paths to the project template files based on the folder structure in the ZIP file.

Providing Guidance

In addition to providing templates, it is often helpful to provide guidance or extra information about how to use the template. One way to do this is to provide well-commented template code files, but this requires a user to dig through your template code to understand how to use it. Visual Studio 2005 templates provide a better mechanism for not only delivering the documentation your users will need, but for making sure they are presented with the documentation after adding an item or project from your template.

In both project and item templates, the ProjectItem element can have several attributes applied to it that allow for displaying items to the user. The OpenInEditor Boolean attribute allows a project item to be opened in the default editor based on the file type of that item. For example, an XML file included in your project and marked with OpenInEditor set to true will be opened in the XML editor. The OpenInWebBrowser attribute allows a text or HTML file to be opened in a Web browser window. This is an easy way to distribute help or guidance with your templates. Simply create an HTML file providing your guidance and include it in the template. Users will view your help file whenever they use your template so you can be sure they have the information they need to properly use your template files once installed.

In addition to having files open in the browser or editor windows, you can control the order in which the items appear when you have multiple items opening. You should use the OpenOrder attribute on the ProjectItem element in order to control the order of the displayed items in Visual Studio. The lower the number supplied for OpenOrder, the higher the priority and the closer to the front the item will be displayed.

Parameter Replacement

In many of the examples so far, parameter replacement has been used to allow for the dynamic creation of item and project names and namespaces. Parameter replacement involves using placeholders in the metadata file or in the included source files to indicate where a certain parameter value should be inserted. When you create a new item or project from a template, Visual Studio inserts actual values for the placeholders. There are several built-in parameters that can be used; it is also possible to add your own parameters to be passed to the wizard for replacement.

For parameter replacement to occur, the ReplaceParameters attribute must be set to true on either the Project element or the ProjectItem element. These are the two main locations where parameter replacement is used. When the wizard processes the project files and the included items, for each item where ReplaceParameters is set to true, the wizard searches through a dictionary of values to see if it has any known keys that match the parameters in the file. Parameters with a match are replaced with the stored values. Figure 10 shows the built-in parameters that are available to be used in metadata files and source files.

Figure 10 Built-In Replacement Parameters

Parameters Description
itemname The user-provided name from the dialog. This value is often used for naming classes and files.
safeitemname The same as itemname but with all unsafe characters removed.
safeitemrootname The name of the root item, which can be used for multi-item templates. For example, if you have partial classes, or a code-beside or code-behind scenario, you can use this item in the secondary files when you need the safeitemname of the main item being created.
projectname The user-provided name of the project for a project template.
safeprojectname The user-provided name of the project for a project template with all unsafe characters removed.
rootnamespace The root namespace of the project for an item template, which can be used to replace the namespace in a project item source file.
guid[1-10] A GUID that can be used as a unique identifier for uses such as project GUID in the project file. You can specify up to 10 different GUID parameters using the syntax: guidx, where x is a number between 1 and 10.
time The current time in the format DD/MM/YYYY HH:MM:SS.
year The current four-digit year.
username The Windows username of the logged-in user.
userdomain The Windows domain of the logged-in user.
machinename The name of the machine where the template is being used.
clrversion The version of the common language runtime being used.
registered-organization The registered organization of the user based on the system settings.
wizarddata A single string that can be any XML or string data included in the WizardData element in a template metadata file.

Custom parameters can also be added to the template and are then available in the item templates and metadata file as well. To add custom parameters, you add a CustomParameters section within the TemplateContent element of the metadata file. For each parameter you want to add, you can specify a CustomParameter element with a name and the value (see Figure 11). Name your values using the $parametername$ syntax in this file in order to use the same naming convention in the source files.

Figure 11 Using the CustomParameter Element

<VSTemplate Version="2.0.0" xmlns="https://schemas.microsoft.com/developer/vstemplate/2005" Type="Item"> <TemplateData> ... </TemplateData> <TemplateContent> ... <CustomParameters> <CustomParameter Name="$mycustomparameter$" Value="ExampleValue" /> </CustomParameters> </TemplateContent> </VSTemplate>

Extending the Wizard

If all the extensibility and parameter replacement is not enough to meet your needs and you really have to write some code to provide the dynamic data you need for your template, you can write a wizard extension and configure it in the template metadata. An example of when you might need this type of extension is a case where you have several items in a template and need to be able to include or exclude some at run time. With a wizard extension, you can augment the standard New Project or New Item wizard with your own code and make decisions about what items to include or exclude as well as adding dynamic custom parameters for replacement in the project files.

In order to create your own wizard extension, you need to create a .NET library project in your managed language of choice and add a reference to the Microsoft.VisualStudio.TemplateWizardInterface.dll and EnvDTE.dll assemblies. Create a class in your library project and implement the Microsoft.VisualStudio.TemplateWizardInterface.IWizard interface. The IWizard interface members are listed in the following code:

public interface IWizard { void BeforeOpeningFile(EnvDTE.ProjectItem projectItem) ; void ProjectFinishedGenerating(EnvDTE.Project project); void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem); void RunFinished(); void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParameters); bool ShouldAddProjectItem(string filePath); }

The first three methods provide access to the IDE extensibility model for the items that have been added and might be opening. The RunFinished and RunStarted methods provide a means for running initialization and clean-up code. RunStarted is a common method to provide an implementation for and to write logic that sets flags used by the ShouldAddProjectItem method; these flags will determine whether an item from the template should be added to the project.

In order to configure the extension, you must modify the VSTemplate element in the template metadata file to add a WizardExtension element that defines the assembly information for your custom wizard extension. The abbreviated template file in Figure 12 shows how this configuration works. When the wizard is run, the wizard extension will be loaded and its methods called at the appropriate time to augment the wizard processing.

Figure 12 Using the WizardExtension Element

<VSTemplate Version="2.0.0" xmlns="https://schemas.microsoft.com/developer/vstemplate/2005" Type="Item"> <TemplateData> ... </TemplateData> <TemplateContent> ... </TemplateContent> <WizardExtension> <Assembly>MSDNMagazine.TemplateWizardExtension.dll</Assembly> <FullClassName> MSDNMagazine.TemplateWizardExtension.ExampleOrgWebPageExtension </FullClassName> </WizardExtension> </VSTemplate>

Conclusion

Visual Studio 2005 has greatly improved the template capabilities and the ease with which you can create reusable, dynamic templates to provide robust starting points for projects and project items. The use of a simple ZIP file and an XML metadata file make customizing templates straightforward. In addition, the export features in Visual Studio make starting a template as simple as opening an existing project. The samples available for download provide several simple illustrations of the items discussed in this article put to use. See the Visual Studio Template Schema Reference in the MSDN® Library for additional details about adding customization to your template metafiles.

Matt Milner is an independent software consultant specializing in Microsoft technologies including .NET, Web services, XML, and BizTalk Server. As an instructor for Pluralsight, Matt teaches courses on Web services, BizTalk Server and ASP.NET. Matt lives in Minnesota with his wife Kristen and his son Max.