Download the code for this article here.
Earlier this year I authored an open-source toolkit, called Simple MVVM Toolkit, to help developers build Silverlight, WPF and Windows Phone applications based on the Model-View-ViewModel design pattern. To enhance developer productivity, the toolkit combines a set of helper classes with code and xml snippets, as well as Visual Studio item and project templates. After installing the toolkit, all a developer needs to do to get started is open Visual Studio and create a new project by selecting one the project templates that appear under the Mvvm category.

Visual Studio makes it extremely easy to create a single-project template. Simply select Export Template from the File menu and follow the prompts. This will produce a .zip file containing the contents of the project and a .vstemplate text file with xml describing the template content and properties.

There are times, however, when it you would like to create a template which generates multiple projects belonging to a single Visual Studio solution. A good example is the SimpleMvmRiaServices project template shown in the first screenshot, which generates a Visual Studio solution with three projects: an ASP.NET Web project, a Silverlight client project, and a Test project for unit tests. To create this template I first had to create individual templates for each of the three projects, then extract the contents of each zip file in order to combine them into a single multi-project Visual Studio template, with a .vstemplate file describing the contents. Here is an example of a VSTemplate file I created for a Multi-Project WCF REST Template I recently did for a talk I gave at a .NET developer group (some of the content has been elided for clarity).
<VSTemplate Type="ProjectGroup"> <TemplateData> <Name>WCF REST Service</Name> <Description>REST Service Template by Tony Sneed</Description> <ProjectType>CSharp</ProjectType> <TemplateGroupID>WCF</TemplateGroupID> <RequiredFrameworkVersion>4.0</RequiredFrameworkVersion> </TemplateData> <TemplateContent> <ProjectCollection> <ProjectTemplateLink ProjectName="$safeprojectname$.Client"> Client\Client.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.Entities"> Entities\Entities.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.Service"> Service\Service.vstemplate </ProjectTemplateLink> <ProjectTemplateLink ProjectName="$safeprojectname$.Web"> Web\Web.vstemplate </ProjectTemplateLink> </ProjectCollection> </TemplateContent> <WizardExtension> <Assembly>RestTemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c9a76f51a8a9555f</Assembly> <FullClassName>RestTemplateWizard.RootWizard</FullClassName> </WizardExtension> </VSTemplate>
The template encompasses four project templates: Client, Entities, Service and Web. Notice the variable, $safeprojectname$, which captures what the user entered for the project name in the New Project dialog. There is also a WizardExtension element, which references an IWizard implementation. IWizard allows you to control the template creation process, for example, to allow projects to reference one another or to set project properties, such as the RIA Service project link required for my SimpleMvvmRiaServices template.
For my WCF REST template I wanted the Client and Service projects to both reference the Entities project, and for the Web project to reference the Service project. The problem here is that within each child template, $safeprojectname$ represents the current project name. There needs to be a way to pass in the root $safeprojectname$ from the parent template. This is where the IWizard project comes in.
Start by creating a class library project and referencing the assembly, Microsoft.VisualStudio.TemplateWizardInterface.dll. Create a RootWizard class that implements IWizard. In the RunStarted method you can capture $safeprojectname$ from replacementParameters and store it in a public static Dictionary<string,string> so that it can be captured by a ChildWizard class and stored as $saferootprojectname$ in replacementParameters.
public class RootWizard : IWizard { // Use to communicate $saferootprojectname$ to ChildWizard public static Dictionary<string, string> GlobalDictionary = new Dictionary<string,string>(); // Add global replacement parameters public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams) { // Place "$saferootprojectname$ in the global dictionary. // Copy from $safeprojectname$ passed in my root vstemplate GlobalDictionary["$saferootprojectname$"] = replacementsDictionary["$safeprojectname$"]; } // Other members elided for clarity }
public class ChildWizard : IWizard { // Retrieve global replacement parameters public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams) { // Add custom parameters. replacementsDictionary.Add("$saferootprojectname$", RootWizard.GlobalDictionary["$saferootprojectname$"]); } // Other members elided for clarity }
You can now edit project references in child project csproj files, as well as using directives in source code files, to include $saferootprojectname$ where you want the user-entered project name to appear.
<Project ToolsVersion="4.0" DefaultTargets="Build"> <ItemGroup> <ProjectReference Include="..\$saferootprojectname$.Entities \$saferootprojectname$.Entities.csproj"> <Project>{3EC4AE2B-892C-42FC-9814-D46EB06A22E4}</Project> <Name>Entities</Name> </ProjectReference> </ItemGroup> </Project>
using System; using $saferootprojectname$.Entities; namespace $safeprojectname$ { class Program { static void Main(string[] args) { // Client code goes here ... } } }
Next, make sure to sign your wizard assembly, using a test certificate if you like, and then obtain the public key token by opening a Visual Studio command prompt at the location of your wizard dll and entering the command (replace WizardAssembly with your dll file name):
sn –T WizardAssembly.dll

Now that you’ve created a multi-project Visual Studio template, you need a way to deploy it. The best way is to create a VSIX project, which allows you easily publish your template to the Visual Studio Extensions Gallery, so that developers from around the world can install it by selecting Extension Manager from the Tools menu right from within Visual Studio! First you’ll need to install the Visual Studio 2010 SP1 SDK (assuming you’ve first installed SP1 for VS 2010). Then create a new VSIX project (in the Extensibility category that now appears in the New Project dialog). What you get is a nice designer for editing the .vsixmanifest file, where you can set properties and add content.

There are two items you will want to add as content. One is the template wizard assembly you just created, and the other is a zip file containing the multi-project template contents, including both the parent .vstemplate file and icons, as well as a folder for each child project template containing the project contents and .vstemplate file.

The important thing to note here is that the assembly for the IWizard implementation need not be installed into the Global Assembly Cache when it is packaged into the same VSIX project as the templates using it. Another thing you might want to do when adding the template zip file is specify the category under which you want your template to appear in the Visual Studio New Project dialog. In the the case of my WCF REST Template, I have indicated it should appear under the WCF category.

When you build the extensibility project, you’ll get a .vsix file that you can upload to the Visual Studio Extensions Gallery, so that it will appear in the Online Gallery when developers select Extension Manager from the Tools menu. Enjoy.

You can download the code for this article here.










