Developing Visual Studio Project Wizards

While creating customized Visual Studio project templates alone can be useful in providing some boilerplate code and in eliminating the need to perform some repetitive operations (such as adding references to .NET assemblies), the real power of Visual Studio project templates becomes evident when they are combined with Visual Studio wizards. When used with a wizard, a Visual Studio template can create a project whose content reflects conditions specified by the template user at design time.

Visual Studio 2005 features a new object model for developing wizards, as well as a new method of integrating wizards with templates. Unfortunately, in the online help included with Visual Studio .NET, the documentation for its previous versions remains very prominent, while the new documentation is difficult to find. In addition, although large portions of the object model for developing wizards in previous versions still work in Visual Studio 2005, other portions do not.

The Wizard Object Model

Wizards that extend Visual Studio 2005 templates are developed as class library projects. The Visual Studio 2005 extensibility model requires that the class that provides the implementation code implement the IWizard interface. The methods of IWizard are automatically called by Visual Studio at various points as it creates a project from your template. These callback methods give you access to the Visual Studio object environment--which is represented by the DTE2 interface, which in turn represents the application-level object in the object model--and to the VSProject interface, which provides access to elements of the project created by the template. Since IWizard is the central element of the object model, we'll begin by examining it in detail.

The IWizard Interface

The IWizard interface, which is defined in the Microsoft.VisualStudio.TemplateWizard namespace and is found in the Microsoft.VisualStudio.TemplateWizard.dll assembly, defines six callback methods that are invoked by Visual Studio as it generates a project or a project item from a template. Each of these six methods must be implemented in your code, even if you only provide a stub implementation (that is, if you provide a method definition but don't actually include any code). The six callback methods are defined in the following subsections.

The RunStarted Method

This method is called by Visual Studio whenever any template that includes a wizard is first started. The RunStarted method serves as the entry point into wizard code. The method has the following signature:

Its parameters reflect the items of information that Visual Studio provides about the Visual Studio environment, the project, and the template. These are:

automationObject

An argument of type Object that represents the Visual Studio application. It in turn is cast or converted to an EnvDTE80.DTE2 object, the application-level object in the Visual Studio extensibility object model, and is typically assigned to a global variable. For example, the following Visual Basic code provides a skeletal framework for caching the reference to the Visual Studio application object:

Public Class MyWizard : Implements IWizard
Private vsApp As EnvDTE80.DTE2
Public Sub RunStarted(ByVal automationObject As Object_
ByRef replacementsDictionary As Dictionary(Of String, String), _
ByVal runKind As WizardRunKind, _
ByVal customParams As Object) _
Implements IWizard.RunStarted
If TypeOf automationObject Is DTE2 Then
Me.vsApp = DirectCast(automationObject, DTE2)
Else
Throw New ArgumentException("Unable to get a reference to the Visual Studio environment. ")
End If
End Sub
End Class

replacementsDictionary

The replacements dictionary defines the replaceable string parameters used for a project. (For details on replaceable string parameters, see Creating Visual Studio Project Templates.) The replacementsDictionary parameter makes these replaceable string parameters programmatically available to the wizard, and allows the wizard to modify the value of existing parameters, as well as to add new items to the dictionary.

The replacements dictionary is available to the RunStarted method as a generic Dictionary object whose keys and values are both strings. The key consists of the parameter name enclosed in dollar-sign symbols (such as $projectname$). You can iterate the dictionary using Visual Basic code like the following:

For Each entry As KeyValuePair(Of String, String) In replacementsDictionary
' Do something with the key/value pair.
' Its key is returned by the entry.Key property.
' Its value is returned by the entry.Value property
Next
or using C# code like the following:
foreach (KeyValuePair<string, string> entry in replacementsDictionary)
{
// Do something with the key/value pair.
// Its key is returned by the entry.Key property.
// Its value is returned by the entry.Value property
}

You can also retrieve a value by its key name by using the Dictionary object's Item property (in Visual Basic) or its indexer (in C#). However, the attempt to retrieve a key that is not defined in the dictionary throws a KeyNotFoundException. To prevent this, you call the Dictionary object's ContainsKey method beforehand to verify that the key exists.