Stephen Rakonza

Subscribe to Stephen Rakonza: eMailAlertsEmail Alerts
Get Stephen Rakonza: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Article

.NET Feature — Creating Templates for Visual Studio 2005

Easy authoring, single file distribution, and file copy installation

Additional replacement parameters can be added by the developer either through a wizard extension component (will be discussed later in this article) or by adding one or more CustomParameter elements within the TemplateContent section in the .vstemplate file.

<TemplateContent>
    ...
    <CustomParameters>
       <CustomParameter Name="$Parameter1$" Value="Value1"/>
       <CustomParameter Name="$Parameter2$" Value="Value2">
    </CustomParameters>
</TemplateContent>

By customizing the .vstemplate file, a template created from the Export Template wizard can be further refined to fit the user's needs. For example, the MDI application template included in the sample for this article contains various customizations. Customizing the .vstemplate file is sufficient for many templates with basic functionalities. And it is usually the first step toward creating templates with advanced features.

Using Wizard Extension Components in the Template
Templates with advanced features usually require custom code to be executed when they are invoked by Visual Studio 2005. The custom code can be used to display a custom UI, to dynamically alter parameter replacement, control the generated file list and perform actions allowed by the Visual Studio automation object model. Many project templates and item templates include a UI wizard to gather user input to customize the generated project. Such functionalities are implemented through a wizard extension component.

Adding a wizard extension component to a project template or item template is relatively straightforward. It involves two steps:

  1. Create the wizard extension component and add it to the global assembly cache, or the GAC. The wizard extension component is a component implementing the IWizard interface. This interface is used by Visual Studio 2005 template engine to load and invoke wizard extensions.
  2. Add a WizardExtension element in the .vstemplate file to link the template to the wizard extension component created in step 1.
In the next section, we will illustrate this process with the MDI (Multiple Document Interface) application template sample.

The MDI Application Sample Template
This sample template contains two templates: a project template that sets up a MDI application project, and an item template that allows adding a MDI child form to the project. Figure 4 shows an application created from this sample template.

The sample MDI application template can be downloaded from this article's link under http://www.sys-con.com. It contains the complete source code and generated templates. The project template is contained in file MDIProj.zip. It is composed of the following files:

  • MyTemplate.vstemplate: This file contains the metadata for the project template.
  • MDIApplication.csproj file: This is the project file for the generated project.
  • ParentForm.cs, ParentForm.Designer.cs and ParentForm.resx: Contains code for the MDI parent form.
  • AboutBox.cs, AboutBox.Designer.cs and AboutBox.resx: Contains code for the about dialog box.
  • Files that make up the rest of the project, such as Program.cs and AssemblyInfo.cs.
The MDI application project template is designed to offer two user customization options: 1) whether the Edit menu items are present in the generated project and 2) whether the About box is present in the generated project. These require using a wizard extension component to accomplish the following tasks:
  1. When the template is invoked by Visual Studio 2005, display a UI as shown in Figure 5 to gather user input.
  2. Depending on the user's choice, the generated project file MDIApplication.csproj should include the right set of files. If the Has About box option is selected, file AboutBox.cs, AboutBox.Designer.cs and AboutBox.resx will be included in the generated project. Otherwise, these files will not be included in the generated project.
  3. Based on the user input, the generated code file (ParentForm.cs and ParentForm.Designer.cs) should have the right content. If the Has Edit menu option is selected, ParentForm.cs and ParentForm.Designer.cs will include code that creates the Edit menu item and the corresponding event handlers. Otherwise, such code will not be generated. The Has About box option is implemented similarly.
The IWizard interface has various methods to allow a wizard extension component to implement the above requirements. First, the IWizard.RunStarted method will be called when the project template is invoked. This is where the wizard UI should be displayed to collect user input.

void IWizard.RunStarted(object automationObject,
     Dictionary<string, string> replacementsDictionary,
     WizardRunKind runKind,
     object[] customParams)
{
   // Display the wizard UI to gather user input.
   // If the user cancels the UI, return to the new project dialog.
   ProjectOptionForm optForm = new ProjectOptionForm();
   if (optForm.ShowDialog() != DialogResult.OK)
     throw new WizardBackoutException();

   this.hasEditMenu = optForm.HasEditMenu;
   this.hasAboutBox = optForm.HasAboutBox;

In this sample code, type ProjectOptionForm implements the project template option UI as shown in Figure 5. The user input is retrieved and stored in the Boolean properties ProjectOptionForm.HasEditMenu and ProjectOptionForm.HasAboutBox. If the user presses the Cancel button on the option UI, the wizard will throw the WizardBackout exception. This exception will cause Visual Studio 2005 to cancel this operation and return to the New Project dialog.

After collecting the user input, the next task is to generate the project file MDIApplication.csproj with the correct content. If the user chooses to include the about box, the project file MDIApplication.csproj will include project items AboutBox.cs, AboutBox.Designer.cs and AboutBox.resx. Otherwise, these project items will not appear in the project file. To accomplish this, the ItemGroup section in the project file MDPApplication.csproj should contain replacement parameters so that the about box items can be inserted.

<ItemGroup>
$AboutBoxCodeProjectItem$
   <Compile Include="ParentForm.cs">
     <SubType>Code</SubType>
   </Compile>
   <Compile Include="ParentForm.Designer.cs">
     <DependentUpon>ParentForm.cs</DependentUpon>
   </Compile>
$AboutBoxResxProjectItem$
   <EmbeddedResource Include="ParentForm.resx">
     <SubType>Designer</SubType>
     <DependentUpon>ParentForm.cs</DependentUpon>
</EmbeddedResource>
...

If the user chooses to include the about box, the value of the parameter $AboutBoxCodeProjectItem$ will include AboutBox.cs and AboutBox.Designer.cs, and the value of the parameter $AboutBoxResxProjectItem$ will include AboutBox.resx. Otherwise, the value of both parameters will be set to the empty string. This logic is also implemented inside the IWizard.RunStarted method. (See Figure 6)

One of the parameters passed to IWizard.RunStarted method is the replacement dictionary object Dictionary<string, string> replacementsDictionary. This replacement dictionary object is a collection of parameter name and parameter value pairs. It contains all the replace parameters used by the Visual Studio 2005 template engine. Modify this object to dynamically change the value of the replacement parameters, and thusly, affect the final code generated. The sample code below adds new replace parameters to the replacement dictionary object based on the user input.

// Replace parameters can be added dynamically in this function, as demostrated in the code below.
if (!this.hasAboutBox)
{
     replacementsDictionary.Add("$AboutBoxCodeProjectItem$", "");
     replacementsDictionary.Add("$AboutBoxResxProjectItem$", "");
}
else
{
replacementsDictionary.Add("$AboutBoxCodeProjectItem$", "<Compile Include=\"AboutBox.cs\">\n"
+ " <SubType>Form</SubType>\n"
+ " </Compile>\n"
+ " <Compile Include=\"AboutBox.Designer.cs\">\n"
+ " <DependentUpon>AboutBox.cs</DependentUpon>\n"
+ " </Compile>\n");
replacementsDictionary.Add("$AboutBoxResxProjectItem$",
" <EmbeddedResource Include=\"AboutBox.resx\">\n"
+ " <SubType>Designer</SubType>\n"
+ " <DependentUpon>AboutBox.cs</DependentUpon>\n"
+ " </EmbeddedResource>\n");
}

The above approach has file names such as AboutBox.cs coded in the implementation. This limits the use of the wizard extension component to only C# language templates. To avoid this limitation, we can put the parameter values inside the CustomParameters element in the .vstemplate file. The wizard extension component simply checks the user input and clears the replace parameters if necessary. This approach is demonstrated through the dynamically generated About Box handler code.

If the user chooses not to use the about box, the About Box menu item handler code should be removed from file ParentForm.cs. To make the handler code optional, it needs to be replaced by a replacement parameter $AboutMenuItemHandler$ in file ParentForm.cs. The handler code will be moved into a custom parameter element in the .vstemplate file.

<CustomParameters>
   <CustomParameter Name="$AboutMenuItemHandler$" Value=
   "private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
   {
     AboutBox aboutBox = new AboutBox();
     aboutBox.ShowDialog();
   }"/>
</CustomParameters>


More Stories By Xin Yan

Xin Yan has been a software design engineer at Microsoft for over 7 years. He works on Visual Studio developer tools platform team.

More Stories By Stephen Rakonza

Stephen Rakonza has been a software design engineer at Microsoft for over 9 years. He works on Visual Studio developer tools platform team.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.