Creating a VSIX Deployable Project (or Item) Template with Custom Wizard Support

Every now and then, I get an interesting question or problem from a customer (thanks Uma) that highlights a need for some additional documentation, or at the least a sample or walkthrough to illustrate how to get from A to Z. In this instance, the question was "How do you localize a project (or item) template, to support multiple languages?". Better yet, what if that template uses a custom wizard assembly, and you want to deploy it with a VSIX package?

Given all the moving pieces involved, I've opted to break this down into a couple of different blog entries. The first being this one, which will walk you through creating the initial project. And then a follow up blog entry, that will detail the steps to localized the template, wizard assembly, and VSIX package.

Prerequisites

To follow this walkthrough, you will need to install the Visual Studio SDK that matches the version of Visual Studio you are using. If you aren't sure where to find it, there is a list of useful links on the very first entry of this blog that I keep up to date with links to various downloads and resources. Note, while I used VS 2013 Ultimate to create this project, this same technique can be used for VS 2012 and VS 2010 (Pro or better SKUs).

Getting Started

Upon installing the Visual Studio SDK, the following project templates are added to assist you in developing new project and item templates.

  • C# Item Template
  • C# Project Template
  • Visual Basic Item Template
  • Visual Basic Project Template

These Project templates create a baseline project or item template, that when built, produces a .zip file that you can manually copy into your ...\Documents\Visual Studio 20xx\Templates directory, and then used to create a new project, or add a new item to an existing project.

The Visual Studio SDK, also includes a stand-alone "VSIX Project" template, which can be used to deploy a template (in addition to other assets).

These VSIX projects do not generate an assembly by default. But if you manually edit a few properties in the .csproj, you can use the VSIX project to build an assembly and have it included as a part of the .VSIX payload.

So instead of creating a solution with a structure similar to the following:

   Solution - MyTemplateWithWizard
      - MyProjectTemplate   (produces the template as a .zip file)
      - MyTemplateWizard   (produces an assembly with an IWizard implementation)
      - MyTemplateWithWizardVSIX   (packages the template and assembly into a .VSIX)

You can actually implement the wizard assembly with the VSIX project, simplifying the solution structure such that there are only two projects required. For example:

   Solution - MyTemplateWithWizard
      - MyProjectTemplate  (produces the template as a .zip file)
      - MyProjectWizard   (builds the wizard assembly, packaging it and the template into a .VSIX)

The following is a walkthrough that will show you how to create a VSIX deployable project template with custom wizard support, using just the two projects as described above.

 

Create a new "C# Project Template" project

  1. Select the File.New.Project... menu item, to invoke the "Add New Project" dialog

  2. Select the "C# Project Template" template and enter a project name as shown below

    NewTemplateProject

  3. Click the "OK" button to create the initial template project

 

Add a new "VSIX Project" to the solution

  1. Right click the "Solution" node in the Solution Explorer, and select the Add.New Project... menu item, to invoke the "Add New Project" dialog

  2. Select the "VSIX Project" template and enter a project name as shown below

    NewVISXProject

  3. Click the "OK" button to create and add the VSIX project to the solution

 

Add the template as an asset of the VSIX project.

  1. Open the project's source.extension.vsixmanifest file

  2. In the manifest designer, add a name or company to the "Author:" field

  3. Select the "Assets" tab, then click the "New" button as highlighted below

    VSIXDesigner 

  4. In the "Add New Asset" dialog, select the Type, Source, and Project fields as shown below

    AddNewAsset 
      

Build and Verify

  1. Build the Solution

  2. Launch the VS Experimental Instance with CTRL+F5

  3. Select the File.New.Project... menu item, to invoke the "Add New Project" dialog

  4. Select the "MyProjectTemplate" and create a new project as shown below

    TestRun 

Modify the template.

  1. Change some of the elements displayed in the New Project dialog, by editing the .vstemplate file
    1. Open the .vstemplate file

    2. Change the Name, Description, and DefaultName elements as highlighted below

      <?xmlversion="1.0"encoding="utf-8"?>
      <VSTemplateVersion="3.0.0"Type="Project"
      xmlns=https://schemas.microsoft.com/developer/vstemplate/2005xmlns:sdk="https://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
      <TemplateData>
      <Name>My Project</Name>
      <Description>My Custom Project Template</Description>
      <Icon>MyProjectTemplate.ico</Icon>
      <ProjectType>CSharp</ProjectType>
      <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
      <SortOrder>1000</SortOrder>
      <TemplateID>0504f2de-af7a-4065-a542-f65ca8c61790</TemplateID>
      <CreateNewFolder>true</CreateNewFolder>
      <DefaultName>MyProject</DefaultName>
      <ProvideDefaultName>true</ProvideDefaultName>
      </TemplateData>
      <TemplateContent>
      <ProjectFile="ProjectTemplate.csproj"ReplaceParameters="true">
      <ProjectItemReplaceParameters="true"TargetFileName="Properties\AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
      <ProjectItemReplaceParameters="true"OpenInEditor="true">Class1.cs</ProjectItem>
      </Project>
      </TemplateContent>
      </VSTemplate>  

  2. Rename Class1.cs to something more appropriate.
    1. Right click "Class1.cs" in the Solution Explorer, and select the "Rename" menu item
    2. Rename Class1.cs to MyProject.cs
    3. Open MyProject.cs, and change the classname from Class1 to MyProject, and add a simple comment as shown below

 

AddComment 

  1. Fixup the .vstemplate and .csproj
    1. Open the .vstemplate file, find and rename "Class1.cs" to "MyProject.cs"
    2. Open the template's .csproj file, find and rename "Class1.cs" to "MyProject.cs"
  2. (Optional) Build and Test in the Experimental Instance again

 

Modify the .VSIX project to build a custom wizard implementation.

  1. Modify the project's properties to build an assembly.

    1. Right click the VISX Project node in the Solution Explorer, and select "Unload Project"

    2. Right click the VISX Project node in the Solution Explorer, and select "Edit MyProjectWizard.cs"

    3. Change the following properties from "false" to "true"

      <IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer> 
      <IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer> 
      <IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment>

    4. Save MyProjectWizard.csproj

    5. Right click the VSIX Project node in Solution Explorer, and select "Reload Project"

  2. Add a reference to EnvDTE.dll. Select the envdte node in Solution Explorer, and in the Properties toolwindow, change the "Embed Interop Types" property to "False", to ensure a clean build

  3. Add a reference to the Microsoft.VisualStudio.TemplateWizardInterface.dll

  4. Right click the VSIX Project node in Solution Explorer, and select Add.Class... menu item

  5. Add a new class called MyProjectWizard.cs

  6. Replace the contents of MyProjectWizard.cs with the following

    using System;
    using System.Collections.Generic;
    using EnvDTE;
    using Microsoft.VisualStudio.TemplateWizard;

    namespace MyProjectWizard
    {
    public class MyProjectWizard : IWizard
    {
    private DTE _dte;

    public void BeforeOpeningFile(ProjectItem projectItem) { }
    public void ProjectFinishedGenerating(Project project) { }
    public void ProjectItemFinishedGenerating(ProjectItem projectItem) { }
    public void RunFinished() { }

    public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
    {
    _dte = (DTE)automationObject;

    // add an entry to the dictionary to specify the string used for the $greeting$ token
    replacementsDictionary.Add("$greeting$", "Hello Custom Library");
    }

    public bool ShouldAddProjectItem(string filePath) { return true; }
    }
    }

  7. Open the source.extension.vsixmanifest, select the Assets tab, and click the "New" button

  8. In the "Add New Asset" dialog, select the Type, Source, and Project fields as shown below

    AddNewAsset2

  9. Select File.Save All to ensure all the above changes are saved

  10. Strong Name the Assembly (Wizard assemblies need to be strong named)

    1. Right Click the VSIX Project node in Solution Explorer, and select the Properties menu item

    2. In the Project Designer, select the "Signing" tab

    3. Check the "Sign the Assembly" checkbox as shown below

      Signing

    4. Select "<New...> in the "Choose a strong name key file"

    5. In the "Create Strong Name Key" dialog, enter "key.snk" for the Key filename

    6. Uncheck the "Protect my key file with a password" checkbox, and click the OK button
       

Associate the Wizard with the project template

  1. Run SN.EXE -T on the wizard assembly to retrieve the public token key

  2. Tip: Create an external tool entry on the Tools menu, as described in Jeremiah Clark's blog Visual Studio Tip: Get Public Key Token for a Strong Named Assembly

  3. Open the template's .vstemplate file and add the following <WizardExtension> section

    <WizardExtension>
    <Assembly>MyProjectWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=22c2a1a5fa7b6905</Assembly>
    <FullClassName>MyProjectWizard.MyProjectWizard</FullClassName>
    </WizardExtension>

  4. Open the template project's MyProject.cs file, and replace the comment line with the following

    using System;
    using System.Collections.Generic;
    $if$ ($targetframeworkversion$ >= 3.5)using System.Linq;
    $endif$using System.Text;

    namespace $safeprojectname$
    {
    // $greeting$
    public class MyProject
    {
    }

      

Build and Test the Solution

  1. Build the Solution
  2. Set a breakpoint on the MyProjectWizard.RunStarted method
  3. Launch the Experimental Instance with the debugger (F5)
  4. Select the File.New.Project... menu item, to invoke the "Add New Project" dialog
  5. Select the "My Project Template", and click the OK button
  6. When the breakpoint is hit, select Debug.Continue (or F5) and let the wizard run to completion
  7. Note the new comment added to the resulting MyProject.cs file

 

Summary

And there you have it, a VSIX deployable template with a custom wizard support. When finished, use the resulting .VSIX file to deploy the template to your production environment. And if you think others will find your template useful, consider uploading it to the VS Extension Gallery.

The resulting code from the above walkthrough can be downloaded from here.