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

Sr. Software Solutions Architect, Hilti Global Application Software
This entry was posted in Technical and tagged , . Bookmark the permalink.

69 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

  24. Richard says:

    I am currently finding that the ProjectName from the ProjectGroup template is replacing the $safeprojectname$ in my using statements. Is this why you use $saferootprojectname$ or is that more specific to your wizard?

    • Tony Sneed says:

      $safeprojecteootname$ is specific to my wizard and represents the solution name.

      • Richard says:

        So it’s kind of performing the same function as I described. I have a problem where the names of each project in the main vstemplate is replacing the names in the using statements.

        using $safeprojectname$.service;

        namespace $safeprojectname$

        becomes:

        using test.mvc.service;

        namespace test.mvc

        This should obviously be test.service!

        Am I correct in saying that your wizard method resolves this issue?

      • Tony Sneed says:

        Yes, this will reaolve your issue.

      • Richard says:

        Excellent, thanks very much!

      • Richard says:

        I went round in circles a bit, but under VS 2013 you can achieve the $safeprojectrootname$ functionality without a wizard using $ext_safeprojectname$ and the following in the ProjectGroup vsTemplate file.

        Now to just resolve my nuget bundling in my VSIX and I can call it a day!

  25. Richard says:

    ^ code was emitted..

    ProjectTemplateLink ProjectName=”$safeprojectname$.Example” CopyParameters=”true”

  26. Kidambi says:

    Hi, I created the templates and template wizard in vs2013. I created the vsix file (with the templates and the wizard as assets) and then installed it, All went well. I could also see the template installed under “extension and updates” and under File\new projects. However, when I select the new project and click Ok, Nothing is created. All I see is a status bar at the bottom telling me that the project is created. But the solution explorer is empty and there are no files in windows explorer. I’m not sure what I’m missing. Any guidance would be of great help

    • Tony Sneed says:

      It looks like something is wrong with your project template. You should first test the template by placing the zip file you created for it in your local Documents folder – under Visual Studio 2013\Templates\Visual C#. Once you’ve copied the zip file there, you should be able to execute it via the New Project dialog in Visual Studio. After you’ve tested the project template, then you can make it part of your VSIX project.

  27. Kidambi says:

    Thanks a lot. I finally got it. I forgot to add “projectgroup” to my multi project template. It is now working. I have one more question, if you do not mind. I need to add some solution level items. Would you have ay suggestions? I’m already using wizard extension to do NuGet dependencies for my project.

    • Tony Sneed says:

      Yes, it’s not well documented how to add solution items from a project template. But I found a way to add a ReadMe file to the solution in a Wizard project by interacting with DTE2. You can find the wizard code here on line 65:
      _dte2.ItemOperations.AddExistingItem(destReadMePath);
      Let me know if this works for you.

  28. Krystan says:

    You probably long since forgot about this article but I found it and followed it and got a lot out of it :).

    I would suggest an improvement. Its not obvious that in each template specification you need to specifiy the child template in the wizard xml elements, its obvious when you look at your zip file this is what should be done but it would be an improvement in your article (which is good) if you specify this explicitly.

    Great job in documenting this.

  29. Pingback: How To Add Template In Visual Studio 2012 | Tiriya2

  30. Terry says:

    I know this is a fairly old post now but I’m looking to create a multi project solution and this looks to cover exactly what I need. All is fine except that my inter project references for my created solution are broken as the $saferootprojectname$ is not being replace and so I have references such as
    $saferootprojectname$.Models in my projects.
    I can see that my wizard code is running because when I force an error in the RootWizard RunStart method, I get an error when I try to create a new project using this template. I guess I’ve overlooked something but whatever it is it’s had me scratching my head for most of the day

    • Tony Sneed says:

      Do you know how to debug and set breakpoints with the experimental instance?

      • Terry says:

        No I dont. I have put some logging code in RunStarted in both RootTemplate and ChildTemplate as follows

        string logText = string.Format(“{0},{1}”, DateTime.Now.ToString(“dd/MM/yyyy HH:ss”), “RootWizard_RunStarted”);
        if (!File.Exists(_logPath))
        using (StreamWriter sw = File.CreateText(_logPath))
        sw.WriteLine(logText);
        else
        using (StreamWriter sw = File.AppendText(_logPath))
        sw.WriteLine(logText);

        I get output from Root but not Child. Im just looking into Krystan’s post just above mine to see if I have missed something

      • Tony Sneed says:

        This article describes how you can configure Visual Studio to start an experimental instance, so you can set breakpoints and debug a VSIX extension: http://weshackett.com/2009/11/configure-vsix-project-to-enable-debugging.

      • Terry says:

        Thanks. I managed to get debugging working and, when running from dev environment, all works as expected. My issues must therefore be related to my deployment of vsix.

    • tpalacino says:

      i had the same problem you did. take a look at my previous comment if you want a wizard you can drop in and use. that project i created was based on this blog

    • Terry says:

      I have made some progress with my multi-project solution template. I have a couple of outstanding issues that I would like to overcome.
      1) The structure of the solution created using the template has an additional sub-directory. Ie if I click New Project.. and I select my multi-project template, I get my solution created with all of my projects correctly referencing each other but instead of have a directory structure such as
      MyApp
      MyApp/MyApp.sln
      MyApp/MyApp.Models/MyApp.Models.csproj
      MyApp/MyApp.Repositories/MyApp.Repositories.csproj
      MyApp/MyApp.Services/MyApp.Services.csproj
      etc.

      I get

      MyApp
      MyApp/MyApp.sln
      MyApp/MyApp/MyApp.Models/MyApp.Models.csproj
      MyApp/MyApp/MyApp.Repositories/MyApp.Repositories.csproj
      MyApp/MyApp/MyApp.Services/MyApp.Services.csproj
      etc.

      ie my solution file is not at the same level as my project specific sub-directories

      2) I’m also getting issues with any packages added via nuget. The generated solution does not have a packages directory. If I try to restore I notice a number of packages being pulled down but when I try to subsequently build I get the following error

      This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is ..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props.

      If I copy the packages folder from my template solution to the directory as all of the project sub-directories and build, I get a solution that builds, though I get another packages directory created one level up as well

      Any suggestions on either of the two points would be appreciated

  31. Bernd Petersen says:

    First of all this really is a good blog to make a solid VSIX Extension. Mine works fine :). But i wanted to add another function to my Wizard and have been looking for a solution and did not find something. I was wondering if there is a way to Install the latest NuGet Package Version on creation process of an Item/ProjectTemplate from your Wizard programmatically. Tony do you have any experience there?
    My current solution is to go into the used template folder with customParams[0].ToString, read out all packages.config files in these folders and get the NuGetPackages Id and version.
    Then i try to install the NuGetPackages via IVsPackageInstaller with the newest Version number filtered out of NuGetRepository (https://packages.nuget.org/api/v2).
    My problem at the moment for this solution is that i need the Project object, to Install the Packages via IVsPackageInstaller, which seems to be acessible via the RunStarted(object automationObject, …..), saying that i need the “Project” object from the created project. First i thought to access it via ProjectFinishedGenerating(Project project){} but the parameter project always has the value null. I have no experience with EnvDTE objects and in some blogs i could only read that there are possibilities to convert the automationObject into the Project object via EnvDTE. Can you help?

    This is my first post and i have not that much experience in programming. If anything is missing or you need further information just say and i will provide it. Any help or suggestions would be appreciated.

    • Tony Sneed says:

      @Bernd:

      I was wondering if there is a way to Install the latest NuGet Package Version on creation process of an Item/ProjectTemplate from your Wizard programmatically.

      See this article.

      First i thought to access it via ProjectFinishedGenerating(Project project){} but the parameter project always has the value null.

      See this comment with a link to an article on debugging a VSIX.

      I have no experience with EnvDTE objects and in some blogs i could only read that there are possibilities to convert the automationObject into the Project object via EnvDTE.

      See this comment for a link to one of my GitHub repos where you can see an example of this.

  32. Dilip Mevada says:

    Nice article,
    I have one question, I want to create one project template in VS 2017 in which I have one solution which contain three project and one solution level folder “submodule”. Please help me on this.

  33. bob o says:

    WCF REST Service
    REST Service Template by Tony Sneed
    CSharp
    WCF
    4.0

    Client\Client.vstemplate

    Entities\Entities.vstemplate

    Service\Service.vstemplate

    Web\Web.vstemplate

    RestTemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c9a76f51a8a9555f
    RestTemplateWizard.RootWizard

  34. Yuriy says:

    Thanks for your post! I have a problem by adding file in . Could you tell me please how I can do it?

    • Yuriy says:

      I have a problem by adding file (file isn’t referring to any project, it’s should be just in solution folder) in SolutionFolder.

  35. jasonk says:

    Cool article. Unfortunately, the link to your zip file no longer works. Can you please update it? Thank you.

Leave a reply to Tony Sneed Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.