codeslower.com Savor Your Code.

Adding Custom Project and Item Templates to Visual Studio 2008

Visual Studio has an extremely rich and complex extensibility model. Recently I have been working on a creating a project "flavor," which extends the Visual C# project system. A project flavor, or subtype, inherits from an existing project. From here on I will refer to this type of project as a project "subtype."

I need to support two areas of customization -- adding a custom project template to the "Create New Project" dialog and adding custom item templates to the "Add New Item" dialog. The documentation on this area is extensive but unfortunately it does not put all the pieces together for you. This article is intended to serve that purpose.

An example project built using the technique below is available on github at:

http://github.com/m4dc4p/flavorsample/tree/master

which can be cloned from:

git://github.com/m4dc4p/flavorsample.git

Basics

First, let me introduce some terms and concepts I'll use in this post.

Package Project

A "Visual Studio Integration Package" is the starting point for developing a project subtype. The project and item templates developed will be called "templates." When referring to the package being developed, I will call it the "package project." There are several key classes that must be present in your package:

VSTemplate files

Project and item templates depend on so called "VSTemplate" files. These are a replacement for earlier "VSDir" files and are much more descriptive. They are used to describe one or more files that will be used when creating a new project or adding items to an existing project. Visual Studio uses them extensively for its own projects and has good support for creating them from existing files and projects.

Adding a Project Template (Theory)

According to the documenation adding a new project template requires these steps:

  1. Create a folder named "ProjectTemplate" in your package project.
  2. Take an existing project file and modify it for your needs. Place this file in the "ProjectTemplate" folder.
  3. Create a file named "ProjectTemplate.vstemplate" and place it in the "ProjectTemplate" directory. I will not go into the content of this file - it is amply described in the SDK documentation.
  4. Add the "ProvidesProjectFactory" attribute the the package class in your package project. It should look something like this:

    [ProvideProjectFactory(typeof(MyPackageProjectFactory), "MyPackage",
      "MyPackageProject Files (*.csproj)", null, null,
      @"..\..\ProjectTemplate")]
    

    The typeof(MyPackageProjectFactory) statement is very important as it associates your package with its factory.

Because this project is a "subtype" of the C# project, you should see your project template under the "Visual C#" node after building your package and running Visual Studio. Wouldn't that be nice?

What Goes Wrong

After following the above steps, you'll notice several things are amiss:

  • When you click the "MyPackage" node, all the files in the "ProjectTemplate" directory show up.
  • Clicking the "Project" template does not show a description in the status bar. Creating a project based on the template does not follow any naming conventions you might have set.

As near as I can tell, this behavior is a bug. Fortunately you can work around it.

Adding a Project Template (Reality)

The following will tell you how to add a project template in its own folder under the "Visual C#" node in the "Add New Project" dialog. I don't think it's possible to both add your templates under their own node and to use the VSTemplate files. If VSDir files are all you need, the above probably will work. For the rest, read on.

  • Make sure the Properties window is visible ("View | Properties Window" in the menu bar).
  • Select all the files in the previously created "ProjectTemplate" folder.
  • In the Properties window, change the "Build Action" to "ZipProject".
  • Remove the reference to your template directory in the ProvideProjectFactory attribute on your MyPackage class:

    [ProvideProjectFactory(typeof(MyPackageProjectFactory),
        "MyPackage", "MyPackageProject Files (*.csproj)", null, null, null)]
    

The above makes sure Visual Studio does not generate a registry entry which points to the files in "ProjectTemplate." The "ZipProject" build action will do better. It copies templates and other files to Visual Studio's custom templates location, ensuring they show up in the "Add New Project" dialog. However, we have to give it a subdirectory to copy those files to and that requires that the package project file be edited by hand:

  • Unload your package project file (right click on the project node in the solution explorer and select "Unload Project").
  • Right-click on the unloaded project and select "Edit".
  • Find every occurence of "ZipProject" and add this child tag:

    <OutputSubPath>CSharp\MyPackage</OutputSubPath>
    

    Be aware that most of the ZipProject tags are self-closed, e.g.:

    <ZipProject Include="ProjectTemplate\Project.vstemplate" />
    

    After the above step, the tag will look like:

    <ZipProject Include="ProjectTemplate\Project.vstemplate" >
      <OutputSubPath>CSharp\MyPackage</OutputSubPath>
    </ZipProject>
    

    The child element added sets the subdirectory to which the files will be copied. By specifying "CSharp", the template will appear under the "Visual C#" node. The "MyPackage" subdirectory will then appear as a child of the "Visual C#" node, containing your template.

  • Save the file. Right-click it in the Solution Explorer and select "Reload Project".

Build the package again and run Visual Studio. When you create a new project you should see "MyPackage" under the "Visual C#" node, and your project template should now display correctly.

Adding Project Items (Theory)

According to the documentation, these steps should add a custom template to the "Add New Items" dialog:

  • Create an "ItemTemplate" folder in your package project.
  • Add your item template and an appropriate VSTemplate file.
    For example, your directory might contain these files:

    ItemTemplate\MyClass1.cs
    ItemTemplate\Class.vstemplate
    
  • Add a ProvidesProjectItem attribute to your package class, like so:

    [(ProvideProjectItem("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}",
     "MyPackage", @"..\ItemTemplate", 100)]
    

    That GUID refers to the C# project and ensures your items show up under the "Visual C#" node.

Again, build your package and run Visual Studio. Create a new project based on your template. Right click on the project node and select "Add New Item." Not surprisingly, things don't look so well.

What Goes Wrong?

Your items will appear under the "MyPackage" node, but much like before, Visual Studio seems to ignore the VSTemplate file. This also appears to be a bug. Now, I'll tell you how to work around it.

Adding Project Items (Reality)

This procedure is very similar to the one for Project templates. You will not be able to add your items to a node at the same level as the "Visual C#" node in the "Add New Items" dialog, but you will be able to add them in a custom subfolder.

  • Make sure the Properties window is visible.
  • Select all the files in your "ItemTemplate" folder.
  • Set "Build Action" in the properties window to "ZipItem".
  • Remove the ProvideProjectItem attribute from your package class. It's useless.

Now the project file must be edited by hand, just like above.

  • Right click your package's project file and select "Unload Project".
  • Right click the unloaded project again and select "Edit."
  • Find all instances of ZipItem and add a child element like this:

    <OutputSubPath>CSharp\MyPackage</OutputSubPath>
    
    As above, most of these elements will be self closing so be sure you have updated them correctly. The path specified behaves just like the one above for projects. Your items will appear under the "MyPackage" node, which itself will be under the "Visual C#" node in the "Add New Items" dialog.
  • Reload the project and build again.

Running Visual Studio, opening one of your custom projects should and opening the "Add New Items" dialog should show your items in the right place.

Caveats, etc.

I have not yet deployed my custom package, so I don't know what applies in that scenario. Maybe ProvideProjectItem and ProvideProjectFactory are better behaved.

I am using the Visual Studio 2008 SDK, Service Pack 1. These instructions may or may not apply to other versions.

Please add any corrections or updates in the comments. Better yet, submit a patch. As I said above, it can be cloned from:

git://github.com/m4dc4p/flavorsample.git

Category: None

Please login to comment.

2 Comments

  1. Neat! by Func. N. Stein (2008-11-07)

    Nice thanks!

  2. Question. by Davide (2008-11-20)

    Good Example!!

    After the compilation of the package is neccessary to register it with RegPkg tool?

    I don't understand how to define the innerproject of a flavoured projects, for example: I want to create a flavoured project for a class library project?