One of the really nice features of Visual Studio is the ability to very easily create projects templates from existing projects. This is a great feature to create a base project that contains only the things you need or adds additional things that you use in every project or some specific ones. For me this is especially useful for Web Projects as I have a fairly lengthy lists of support files and dependencies that I suck into new Web projects: stock CSS/image folders, scripts, a couple of assembly references and a host of system features and admin pages.

A typical Web startup template for me looks something like this:

TemplateProject

This is basically an extract from a full blown application and pulling out just the reusable components that are a starting point for a new application. Short of the App, ApplicationConfiguration and AppUtils classes which tend to get modified in every application the other files are essentially static and can easily be copied into this ‘template’ project as is by batch file or post build step of a whatever master project that makes up this template.

One thing that you probably want to do with your templates however is change the namespace for source files. So while my application namespace may be something like Westwind.WebToolkitWeb (for the sample app) may be appropriate for the sample app, but in your application most likely the namespace should match your project. You can use a host of template parameters within your project’s text files to replace text when the template is created. The only one that I typically use in a project template is SafeProjectName which looks like this:

namespace $safeprojectname$
{

    /// <summary>
    /// Global Application class that holds various static and
    /// configuration properties and methods. 
    /// 
    /// This class is meant as a one stop application, configuration
    /// constant and management object that unifies various common
    /// applicaiton tasks in one place.
    /// </summary>
    public class App
    {

}
}

When the project is created SafeProjectName is turned into the actual project name with spaces and punctuation stripped which then effectively becomes the base namespace for your project. It’s easy to replace this value with a project wide Replace In Files for (namespace your.namespace with namespace $safeprojectname$).

Note that this step is not necessary. If you are only working internally with generated projects you can probably just use a hardcoded namespace and that’s fine since you’re not likely to have namespace overlap with another Web project. It might make sense though to use something ‘generic’ for the namespace of any components you stick into the template like Westwind.WebApplication or ApplicationRoot or something along those lines.

Exporting the template

Once you’ve set up the template project the way you want it you can export it and this really is the easy part. Find the project in the Solution Tree and then go to File| Export Template:

ExportTemplate

You’ll be asked a couple of questions starting with the type of template to export – choose project:

TemplateWizardStep1

and for the name of the template file and a description. Note you have to pick the project from the projects in the solution – it doesn’t default to the selected project that you exported on which screwed me a couple of times.

The description is what shows up in Visual Studio. I recommend you use a descriptive name in the filename because that’s what the project item will actually use (not as you would expect the description which shows as a tooltip/status notice):

 WizardStep2[4]

Once you do this the template is exported to the location specified, which is just a holding location. When you automatically import the template it is installed into the actual ‘Community Templates’ folder which typically is:

C:\Users\rstrahl\Documents\Visual Studio 2008\Templates\ProjectTemplates

The output from the template generation is a simple .Zip file which contains your project files plus a VS Content file that describes each of the items in the project.

Once installed the template can be used inside of Visual Studio. This puts the template into a generic project location so it shows up in the New Project dialog like this:ProjectDialog1

The project only shows in the generic Visual C# project tree because that’s where the template was actually created. If you go to the Web project types you’ll find that the template doesn’t show up there unfortunately.

To get it to show up in the Web folder you can move the template from the default location to:

C:\Users\rstrahl\Documents\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\Web

which puts it in the more predictable location.

Since the exported template is simply a .ZIP file you can manually copy the file to this location as well. So if you want to share your template with others just pass them the .Zip file and tell them to install it in the above folder and off they go.

Packaging a Template Project

Having to copy a .Zip file into a specific location is not the easiest of ways to deal with installation especially since Visual Studio default file locations can be changed and so Visual Studio actually includes a packaging mechanism for community components that can copy the files into the right location. The process to do this is pretty simple and involves three steps:

  1. Creating a .vscontent file that describes the template
  2. Zipping up the .vscontent file and your project template .zip file
  3. Renaming the resulting .zip file .vsi

The .vscontent file is simply a descriptive file that holds information about the .zip file with the actual template and it looks something like this:

<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
    <Content>
        <FileName>West Wind West Wind Web Toolkit Web Application.zip</FileName>
        <DisplayName>West Wind West Wind Web Toolkit Web Application</DisplayName>
        <Description>A C# project that contains the base West Wind West Wind Web Toolkit components</Description>
        <FileContentType>VSTemplate</FileContentType>
        <ContentVersion>1.0</ContentVersion>
        <Attributes>
            <Attribute name="ProjectType" value="Visual C#"/>
            <Attribute name="ProjectSubType" value="Web"/>
            <Attribute name="TemplateType" value="Project"/>
        </Attributes>
    </Content>
</VSContent>

Note that here you can also specify where this project template goes. You basically specify that it’s a project template and it goes into the Visual C#/Web folder underneath the templates folder. The project sub type is optional and if leave both the project type and sub type blank the template goes into the default project location just as the default .zip template did.

Once you’ve created this .vscontent file zip it up together with the Project Template.zip file and then rename the resulting .zip file to .vsi.The Explorer pane on the right demonstrates all the files that are used in the process:

ResultingFiles

The West Wind West Wind Web Toolkit Web Application.zip file is the original project template. The .vscontent file of the same name is the content description for the .vsi file. The WestWindWebToolkitProject.zip file is the zipped file of the project .zip and .vscontent file and the WestwindWebToolkitProject.vsi is the final product and self contained project template installer. Note you only need to distribute the .vsi file which contains everything it needs to install the template.

If you double click the .vsi file on any system that has Visual Studio 2008 installed the template installer pops up:

TemplateInstaller

TemplateInstallerComplete

After you run this the template will now be installed in Visual Studio and in the correct location in the Web project section of new items. You can see the final location of the template in the right hand pane of the Explore screen shot above.

Couple of caveats with the .vsi installer. If you for some reason select an invalid template location the installer will fail with a warning that the directory doesn’t exist. It’s crucial that you set up your project with a valid project type that actually exists. If for whatever reason the user has moved the Visual Studio template directories or removed them the installer is not smart enough to create the directory for you which is pretty lame. In that case you have to manually install the template (rename the .vsi file to .zip file, pick out the internal .zip template file and manually copy it to the location). This should be a rare occasion but it actually happened to me first time I tried to install the template because apparently the template directory names have changed between VS 2005 and 2008 and the sample I was looking at used the old names. When in doubt of what the correct names are you can check out other templates in the same folders or in the base VS 2008 installation folders.

Creating the new Project

So now with the template installed we can see it in the right place underneath Web applications and I can create a new project:

NewProjectDialog2

If you follow through with the Wizard you then end up with a new Web project that has all the default files and components installed:

CompletedProject

As you can see all files are there and if you look in the actual code files you also see that the namespaces have been properly replaced.

One Problem: Bin Directory not Copying

Unfortunately there’s one problem with this project: If you have referenced components in the bin directory those references are lost as the bin directory does not copy in the new project template: Here two assembly references in the bin folder are broken:

MissingReferences

Behind the scenes there are two problems: The template exporter fails to export the bin folder altogether. So I figured I can create a custom .vstemplate file and simply add the files in:

 

<VSTemplate Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
  <TemplateData>
    <Name>West Wind West Wind Web Toolkit Web Application</Name>
    <Description>West Wind West Wind Web Toolkit Web Application</Description>
    <ProjectType>CSharp</ProjectType>
    <ProjectSubType>
    </ProjectSubType>
    <SortOrder>1000</SortOrder>
    <CreateNewFolder>true</CreateNewFolder>
    <DefaultName>West Wind West Wind Web Toolkit Web Application</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
    <LocationField>Enabled</LocationField>
    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
    <Icon>__TemplateIcon.ico</Icon>
  </TemplateData>
  <TemplateContent>
    <Project TargetFileName="WestWindWebToolkitWebTemplate.csproj" File="WestWindWebToolkitWebTemplate.csproj" ReplaceParameters="true">
      <Folder Name="_Classes" TargetFolderName="_Classes">
        <Folder Name="Application" TargetFolderName="Application">
          <ProjectItem ReplaceParameters="true" TargetFileName="App.cs">App.cs</ProjectItem>
          <ProjectItem ReplaceParameters="true" TargetFileName="ApplicationConfiguration.cs">ApplicationConfiguration.cs</ProjectItem>
          <ProjectItem ReplaceParameters="true" TargetFileName="AppUtils.cs">AppUtils.cs</ProjectItem>
        </Folder>
      </Folder>
      <Folder Name="bin" TargetFolderName="bin">
        <ProjectItem>Westwind.Web.dll</ProjectItem>
        <ProjectItem>Westwind.Web.xml</ProjectItem>
        <ProjectItem>Westwind.Utilities.dll</ProjectItem>
        <ProjectItem>Westwind.Utilities.xml</ProjectItem>
      </Folder>
      <ProjectItem ReplaceParameters="true" TargetFileName="Web.config">Web.config</ProjectItem>
    </Project>
  </TemplateContent>
</VSTemplate>

and create a custom .zip file to replace the one the auto-export generated that includes the bin folder. But unfortunately that didn’t work either. No bin folder in the target project.

The only way I found to work around this is to add a folder called SupportAssemblies to my project as an included folder and put the support assemblies underneath it. The references then point at these references in SupportAssemblies:

SupportAssemblies

This works, but it’s ugly as hell. The idea would be that the user would move the folder after the project is installed and then reconnect the project references – or more likely – connect to the full project references.

If anybody has a better way to get this done through a project template – even if its manually edited - I’d love to hear it. I think using multiple projects (ie. a Solution template) is the ticket, but this is not something that can be generated by the exporter.

Earlier today I also got a note from Ben Scheirman, who recommended looking at SolutionFactory which has some interesting packaging features that lets you create a solution template rather than a project template. This might be a better way to go as you can then reference assemblies in a sibling folder to the Web Project (like SupportAssemblies). I haven’t had time to check this out more closely but I believe that this should work.

It’s a Wrap

Wrapping up base components into a template project is a nice way to quickly get started with a new application without having to manually assemble and more importantly remember to assemble all the correct components and support files. The basics of the process are straight forward and can be accomplished in just a few minutes. Whether it’s for your own personal reuse or whether you want a more reusable solution for other users of your template it’s a great way to make project startup more efficient.

If you want to play around with this you can check out the West Wind Web Toolkit Web Application Template in the SubVersion Repository as well as the actual projects that the template is based on up the trunk.

Wrap on…