Condividi tramite


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.

Comments

  • Anonymous
    October 08, 2014
    Hello, this is one of  the best tutorials I found for creating Project Template with Wizard. I had an error when trying to get the public token key- I have created the external tool and it gives me this error - MyProjectWizard.dll does not represent a strongly named assembly. Could you please help me . Furthermore, is it possible to create the project template and the wizard in VS Package

  • Anonymous
    November 03, 2014
    Hi Vladimir, Chances are, you didn't configure the wizard assembly to be strong named. You have to create a new key.snk file in the "Signing" tab of the project properties. Then retrieve the public token and add it to the .vstemplate in the template project. I would recommend keeping the template and wizard projects separate from your VSPackage assembly. These are loaded differently and at different times. While it's theoretically possible to add your IWizard implementation to a VSPackage assembly, it's not something I've seen done by the VS team.

  • Anonymous
    January 15, 2015
    Awesome post.  Thanx!  Could you maybe tell me how to add multiple projects to a template?

  • Anonymous
    February 11, 2015
    Two options Hein. 1. Use a Multi-proj template ( msdn.microsoft.com/.../ms185308.aspx ). 2. Implement a custom wizard that adds an additional project after the 1st one is generated. Stay tuned for a new blog entry covering that 2nd option.

  • Anonymous
    March 05, 2015
    Hello; Thanks for all. One question please: Is  necessary to registry the dll wizard in GAC? Regards.

  • Anonymous
    March 12, 2015
    Hi ARROCAL, wizard DLLs do not need to be in the GAC. Note in the walkthrough above, we never place the wizard assembly in the GAC.

  • Anonymous
    April 06, 2015
    Very Much Thank You Sir, Such helpful article. It solve my GAC assembly issue. Previously I was adding Wizard dll in gac, such a hectic task. Now using this solution, It avoid these steps.

  • Anonymous
    April 20, 2015
    The comment has been removed

  • Anonymous
    April 21, 2015
    Guessing you may have missed something Bablu. You could try using the VSX Template Wizard per my latest blog, to automate all these steps : blogs.msdn.com/.../the-vsx-template-wizard.aspx.

  • Anonymous
    April 30, 2015
    Thanks Ed Dore. I got rid of the problem by installing the DLL into GAC initially. But I did not want to have all the people who use the template to have this overhead. I overcame the GACing problem by following the post here.   oncoding.blogspot.com/.../visual-studio-template-wizards-without.html

  • Anonymous
    July 30, 2015
    I have the same issue as Bablu,  the assembly needs registering in the GAC for it to work.  has anyone built this without having to do this?

  • Anonymous
    August 17, 2015
    Bablu and Andrew. I managed to bypass the error by NOT signing the Wizard assembly. Also then put PublicKeyToken=null to vstemplate-file

  • Anonymous
    October 06, 2015
    Thank you so much !!! I almost gave up on the Wizard but this solution works like a charm :D

  • Anonymous
    December 20, 2015
    The comment has been removed

  • Anonymous
    March 07, 2016
    Very useful post. Helped me a lot in creating a custom project template. I am in the process of creating a starter kit kind of custom project , where the user selects a new project and then a new window will open collecting user's choices and then proceed to generate the code according to the selection. The project will create dummy API calls and the user can include his logic. I am  not able to find anything useful to acheive this use case. Could you please help me or give me some suggestions?