Build a Multi-Project Visual Studio Template

Download the code for this article here. (Zip file updated to include source code for VSIX and Wizard projects.)

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.

  smvvm-proj

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.

exp-template

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

sn-pubkey

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.

vsix-1

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.

vsix-2

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.

add-content

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.

ext-gallery

You can download the code for this article here.

Tony Sneed
http://blog.tonysneed.com

About Tony Sneed

Married with three children.
This entry was posted in Technical and tagged , . Bookmark the permalink.

35 Responses to Build a Multi-Project Visual Studio Template

  1. Ralph Moritz says:

    Useful, very detailed post. Thanks!

  2. Ralph Moritz says:

    After installing my VSIX & resarting VS, clicking File > New Project & selecting my multi-project template, I get this error:

    Error: this template attempted to load component assembly ‘XXX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXX’

    I’ve double-checked the PK token & it’s definitely correct in all the .vstemplate files. Do you have any idea what could be causing this?

  3. riezebosch says:

    Shouldn’t the guid match with the referenced project? I’ve added a method in the “RootWizard” which removes and re-adds all project references to resolve this issue..

  4. I cant find the RootWizard class or the RestTemplateWizard assembly anywhere in your zip file. Am I missing something here?

  5. I downloaded the .vsix from the Visual Studio Extensions Gallery. It contained the RestTemplateWizard assembly.

  6. Pingback: Creating Multi-Project Templates « Bitmask

  7. John says:

    I created the Multi-project template and installed the VSIX file. I created a new solution using this extension but I am missing dll references. How can I get the dll references fixed? do I always do it manually, please let me know if there is a way to fix this issue. Thanks

  8. Tony Sneed says:

    Sorry I’ve been so delinquent in responding. I noticed the download file was incomplete, so I updated it to include source code for the VSIX and Wizard projects. The key to getting the project references right is to make sure the relative path is correct and that the root and child wizards are correctly setting the $saferootprojectname$ variable. The project guids also need to match.

    There are a number of manual steps to perform and it’s easy to miss something. So I found it useful to install the wizard assembly into the GAC and test the template by copying the zip file to the appropriate location under Documents\Visual Studio 2010\Templates. Keith’s multi-project solution template tool sounds intriguing, and I’ll be sure to check it out.

  9. hopkinsrush says:

    I’m REALLY not into wheel reinvention, so wanted to take the time to explain why I decided to build something. The premise follows: 1. VS solution files are text files. 2. Working with text files is easy. 3. Creating multi-project solution templates should be easy.

  10. Tony Sneed says:

    Hi Keith, Appreciate your contrib! Building multi-project solution-based templates can get hairy because of all the different text files you need to hand-edit. Great idea to automate this. Have you published it to the VS Extensions Gallery yet?

    P.S. Just posted a template for WCF SOAP services: http://visualstudiogallery.msdn.microsoft.com/2b106597-fda0-43eb-aa21-4f065584ef10.

  11. hopkinsrush says:

    Not yet. It will be my first time too. Can I reach out if I get stuck on that part?

  12. Pingback: WCF SOAP and REST Multi-Project Visual Studio Templates - .NET Code Geeks

  13. karuna says:

    After doing this stuff, when I am trying to use the template Iam getting error “The given Key was not present”

  14. Frank Car says:

    When i tried to create this template for Visual Studio 2005, it doesn’t replace the variable $safeprojectname$ and all the projects are create like $safeprojectname$.Web, $safeprojectname$.Persistence and so on. Any suggestion for this?

  15. Bhavya Shah says:

    I am trying to create a multi-project template in VS2012 and have created the required ProjectTemplates & IWizard implementation assembly.
    As mentioned by you, we need to add the Wizard assembly as CONTENT in VSIX. But in VS2012 VSIX we don’t have CONTENT tab.
    How do I add the IWizard assembly?
    Can you help me with this?

    • Tony Sneed says:

      If you’re using the VS 2012 SDK, there is an Assets tab. For the Type specify Microsoft.VisualStudio.Assembly. For the Source, you can either select the DLL as a file on the system or, better yet, add the wizard project to the solution and set source to Project in the Current Solution. You can then embed it in the TemplateWizard folder.

  16. Bhavya Shah says:

    Thanks Tony. Yup its worked great. :)

  17. Bhavya Shah says:

    Has anyone tried preinstalling NuGet for a multi-project template.
    I am trying to add a NuGet to only 1 project of the 3 project I have in the multi-project template.

    Any pointers will be helpful.

    ~Bhavya

  18. Yoni says:

    I have a multi project template and i want to add a condition in the solution vstemplate file in order to control which project to add, depends on the wizard user input.
    the $if$($paramname$==value) does not work in the solution vstemplate file.
    any ideas ?

  19. I don’t know if anyone is still coming across this blog but i have created an assembly that can be used directly to expose top level template parameters too all the child templates. It also extends the number of Guids available from 10 to 100. You can find the source or just the binaries if that’s all you need here [GlobalParams|https://globalparams.codeplex.com/]

  20. tpalacino says:

    I don’t know if anyone is still coming across this blog but i have created an assembly that can be used directly to expose top level template parameters too all the child templates. It also extends the number of Guids available from 10 to 100. You can find the source or just the binaries if that’s all you need here https://globalparams.codeplex.com/

  21. In my case $saferootprojectname$ never gets resolved, even though it seems to pick up the Wizard. I get $saferootprojectname$ all over my project. :(

  22. Nicolas Michels says:

    Excelent Tutorial!

  23. Pingback: Visual Studio Templates - The BHW Group

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s