WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

Developing Visual Studio Project Wizards
Pages: 1, 2, 3, 4, 5, 6

Creating the Template

Next, create the template that will be used to generate individual projects. To do this, create a console mode application in either C# or Visual Basic. Add a reference to AppLib.dll to the project. Rename the code file to AppClass.cs or AppClass.vb, and replace the contents of the C# code file with the following code:

using MyCompanyApp;
using System;

namespace MyCompanyApp
{
   class AppClass
   {
      static void Main()
   {
      $OpeningMessage$;
   }
   }
}

The corresponding Visual Basic code is:

Imports MyCompanyApp

Module AppClass
   Sub Main()
      $OpeningMessage$
   End Sub
End Module

Notice that $OpeningMessage$ is not valid C# or Visual Basic syntax; instead, this is a replaceable string parameter. When the project is created, the wizard replaces this string with a call to the appropriate library method. The template can then be exported and its .vstemplate file modified, as discussed in the "Modifying the .vstemplate File" section. In addition, you should change the assembly reference, which appears as follows:

<Reference Include="AppLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a68e0a70a91f57a7, processorArchitecture=MSIL">
   <SpecificVersion>False</SpecificVersion>
   <HintPath>..\AppLib\bin\Release\AppLib.dll</HintPath>
</Reference>

to the following:

<Reference Include="AppLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a68e0a70a91f57a7, processorArchitecture=MSIL" />

This insures that, rather than attempting to locate a local copy in a particular directory, Visual Studio will load the assembly from the GAC. Once you've made all of the modifications, you can place the new set of files in a ZIP file and place the file in the Visual Studio project template directory for either Visual Basic or C#.

The Wizard

The wizard simply opens a Windows form that allows the developer to indicate whether the project targets version 1 or 2 of the class library. If the developer chooses version 1, the string AppLib.DisplayGreeting() replaces the replaceable string parameter. If the developer chooses version 2, AppLib.DisplayGreetingDialog() replaces the string parameter. In addition, if the developer chooses version 2, a reference to AppLib2 is added to the project. The following is the code for a class named AppWizard, which provides the IWizard implementation:

Imports EnvDTE80
Imports Microsoft.VisualStudio.TemplateWizard
Imports VSLangProj

Public Class AppWizard : Implements IWizard
   ' Declare private variables
   Private vsApp As DTE2
   Private isVB As Boolean
   Friend targetedVersion As Integer = 1

   Public Sub BeforeOpeningFile(ByVal projectItem As EnvDTE.ProjectItem) Implements Microsoft.VisualStudio.TemplateWizard.IWizard.BeforeOpeningFile

   End Sub

   Public Sub ProjectFinishedGenerating(ByVal project As EnvDTE.Project) Implements Microsoft.VisualStudio.TemplateWizard.IWizard.ProjectFinishedGenerating
      ' Add reference to AppLib2 for V2 projects
      If targetedVersion = 2 Then
         Dim appProject As VSProject = DirectCast(project.Object, VSProject)
         Dim ref As Reference = appProject.References.Add("AppLib2")
         Console.WriteLine(ref.Version)
      End If
   End Sub

   Public Sub ProjectItemFinishedGenerating(ByVal projectItem As EnvDTE.ProjectItem) Implements Microsoft.VisualStudio.TemplateWizard.IWizard.ProjectItemFinishedGenerating

   End Sub

   Public Sub RunFinished() Implements Microsoft.VisualStudio.TemplateWizard.IWizard.RunFinished

   End Sub

   Public Sub RunStarted(ByVal automationObject As Object, ByVal replacementsDictionary As System.Collections.Generic.Dictionary(Of String, String), ByVal runKind As Microsoft.VisualStudio.TemplateWizard.WizardRunKind, ByVal customParams() As Object) Implements Microsoft.VisualStudio.TemplateWizard.IWizard.RunStarted
      ' Get reference to application-level object
      Me.vsApp = DirectCast(automationObject, DTE2)

      ' Open dialog to determine version of library user develops against
      Dim frm As New WizardForm(Me)
      frm.ShowDialog()
      ' Terminate template if user has cancelled form
      If frm.DialogResult = Windows.Forms.DialogResult.Cancel Then
         ' Cancel loading of template (and wizard)
         Throw New WizardCancelledException
      Else
         ' Adjust the method call
         replacementsDictionary.Add("$rootNamespace$", "MyCompanyApp")
         If Me.targetedVersion = 1 Then
            replacementsDictionary.Add("$OpeningMessage$", "AppLib.DisplayGreeting()")
         ElseIf Me.targetedVersion = 2 Then
            replacementsDictionary.Add("$OpeningMessage$", "AppLib2.DisplayGreetingDialog()")
         ' This shouldn't happen
         Else
            Throw New WizardCancelledException("An unexpected error has caused the template to terminate.")
         End If
      End If
   End Sub

   Public Function ShouldAddProjectItem(ByVal filePath As String) As Boolean Implements Microsoft.VisualStudio.TemplateWizard.IWizard.ShouldAddProjectItem
      Return True
   End Function
End Class

The AppWizard class in turn instantiates a form class named WizardForm and passes it a reference to itself. The WizardForm class has a drop-down list box named cboVersions that displays the versions of the AppLib class library. The complete source code for the WizardForm class is:

Public Class WizardForm
   Private Const TOTAL_PAGES = 1

   Dim wizard As AppWizard

   Public Sub New(ByVal wizardClass As AppWizard)
      MyBase.New()
      InitializeComponent()
      wizard = wizardClass
   End Sub

   Private Sub WizardForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      Dim ctr As Integer = 1           ' Counter to track current page (for multi-page wizards)
      Me.cboVersions.SelectedIndex = 0
   End Sub

   Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
      Me.wizard.targetedVersion = Me.cboVersions.SelectedIndex + 1
   End Sub

   Private Sub brnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles brnCancel.Click

   End Sub
End Class

Once you've created the wizard and registered in the GAC, and you've created the template and placed it in the appropriate directory, you can use it to create projects in Visual Studio.

Some Additional Comments

Note that we use the same wizard to handle a Visual Basic and a Visual C# project template. Ordinarily, we'd want to include some means of determining the template's target language so that we can use the individual language's syntax or address features in its Visual Studio development environment. We can do this by retrieving the string returned by the Kind property of the EnvDTE.Project interface, then looking up the value of this key in the registry. It is much easier, though, to define a custom string parameter named Language in the .vstemplate file, and to give it a value of VisualBasic for the Visual Basic template and CSharp for the C# template.

When debugging wizard applications, it's important to remember that both Visual Studio and the GAC use cached copies of their assemblies. This means that when you modify and recompile your assembly, the .NET Framework or Visual Studio may be working with a previous copy. To make sure that the current assembly is being used, it's a good practice to unregister the previous version of an assembly from the GAC and then to register the new version. The instance of Visual Studio that was used to create a template should also be closed once the template is modified, and a new instance started to work with the new one.

Ron Petrusha is the author and coauthor of many books, including "VBScript in a Nutshell."


Return to Windows DevCenter.