installer, they are added to the Application folder. However, you want the primary output
of the class library to be placed in the GAC, and its content files to go into the user’s Visual
Studio Templates folder. To move these files, right-click the Installer project and select
View . File System from the context menu to open the File System view.
2 .
By default, the File System view contains the Application folder (which can’t be deleted), the
User’s Desktop folder, and the User’s Programs Menu folder. Remove the two user folders
by selecting Delete from the right-click context menu.
3 .
Add both the Global Assembly Cache (GAC) folder and the User’s Personal Data folder
(Documents) to the file system by right-clicking the File System on Target Machine node
and selecting these folders from the list.
4 .
Into the User’s Personal Data folder, add a Visual Studio 2010 folder (right-click and choose
Add . Folder), followed by a Templates folder, followed by a ProjectTemplates folder, and
fiGure 15-9
extending Templates .
301
finally followed by Visual C# (if it is a
C# project) or Visual Basic (if it’s a VB
project). The result should look like
what is shown in Figure 15-10.
5 .
To complete the installer, move the
primary output from the Application
folder into the Global Assembly
Cache folder, and then move the
content files from the Application folder to the ProjectTemplates folder. (Simply drag the
files between folders in the File System view.)
iwizard
Now that you’ve completed the installer, you can start work on the wizard class library. As shown
in Figure 15-9, you have a form (ColorPickerForm) and a class (MyWizard). The former is a simple
form that can be used to specify the color of the background of the main form. To this form you
will need to add a Color Dialog control, called ColorDialog1, a Panel called ColorPanel, a Button
called PickColorButton (with the text “Pick Color”), and a Button called AcceptColorButton (with
the text “Accept Color”).
Rather than use the default icon that Visual Studio uses on the form, you can select a more
appropriate icon from the Visual Studio 2010 Image Library. The Visual Studio 2010 Image
Library is a collection of standard icons, images, and animations that are used in Windows, Office,
and other Microsoft software. You can use any of these images royalty-free to ensure that your
applications are visually consistent with Microsoft software.
The Image Library is installed with Visual Studio as a
compressed file called VS2010ImageLibrary.zip. By default,
you can find this under %programfiles%\Microsoft Visual
Studio 10.0\Common7\VS2010ImageLibrary\1033\. Extract
the contents of this zip file to a more convenient location, such
as a directory under your profile.
To replace the icon on the form, first go to the Properties
window and then select the Form in the drop-down list at the
top. On the Icon property, click the ellipsis button (…) to load
the file selection dialog. Select the icon file you want to use and
click OK (for this example we’ve chosen VS2010ImageLibrary\
Objects\ico_format\WinVista\Settings.ico).
Once completed, the ColorPickerForm should look similar to
the one shown in Figure 15-11.
The following code listing can be added to this form. The main logic of this form is in the event
handler for the Pick Color button, which opens the ColorDialog that is used to select
a color.
fiGure 15-10
fiGure 15-11
302 .
chaPter 15 projecT And iTem TemplATeS
Vb
Public Class ColorPickerForm
Public ReadOnly Property SelectedColor() As Drawing.Color
Get
Return ColorPanel.BackColor
End Get
End Property
Private Sub PickColorButton_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles
PickColorButton.Click
ColorDialog1.Color = ColorPanel.BackColor
If ColorDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then
ColorPanel.BackColor = ColorDialog1.Color
End If
End Sub
Private Sub AcceptColorButton_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles
AcceptColorButton.Click
Me.DialogResult = Windows.Forms.DialogResult.OK
Me.Close()
End Sub
End Class
C#
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WizardClassLibrary
{
public partial class ColorPickerForm : Form
{
public ColorPickerForm()
{
InitializeComponent();
PickColorButton.Click += PickColorButton_Click;
AcceptColorButton.Click += AcceptColorButton_Click;
}
public Color SelectedColor
{
get { return ColorPanel.BackColor; }
}
private void PickColorButton_Click(object sender, EventArgs e)
{
ColorDialog1.Color = ColorPanel.BackColor;
if (ColorDialog1.ShowDialog() == DialogResult.OK)
{
extending Templates .
303
ColorPanel.BackColor = ColorDialog1.Color;
}
}
private void AcceptColorButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
The MyWizard class implements the IWizard interface, which provides a number of opportunities
for user interaction throughout the template process. Add some code to the RunStarted method,
which will be called just after the project-creation process is started. This provides the perfect
opportunity to select and apply a new background color for the main form:
Vb
Imports Microsoft.VisualStudio.TemplateWizard
Imports System.Collections.Generic
Imports System.Windows.Forms
Public Class MyWizard
Implements IWizard
Public Sub BeforeOpeningFile(ByVal projectItem As EnvDTE.ProjectItem) _
Implements IWizard.BeforeOpeningFile
End Sub
Public Sub ProjectFinishedGenerating(ByVal project As EnvDTE.Project) _
Implements IWizard.ProjectFinishedGenerating
End Sub
Public Sub ProjectItemFinishedGenerating _
(ByVal projectItem As EnvDTE.ProjectItem) _
Implements IWizard.ProjectItemFinishedGenerating
End Sub
Public Sub RunFinished() Implements IWizard.RunFinished
End Sub
Public Sub RunStarted(ByVal automationObject As Object, _
ByVal replacementsDictionary As _
Dictionary(Of String, String), _
ByVal runKind As WizardRunKind, _
ByVal customParams() As Object) _
Implements IWizard.RunStarted
Dim selector As New ColorPickerForm
If selector.ShowDialog = DialogResult.OK Then
Dim c As Drawing.Color = selector.SelectedColor
Dim colorString As String = "System.Drawing.Color.FromArgb(" & _
c.R.ToString & "," & _
continues
304 .
chaPter 15 projecT And iTem TemplATeS
(continued)
c.G.ToString & "," & _
c.B.ToString & ")"
replacementsDictionary.Add _
("Me.BackColor = System.Drawing.Color.Silver", _
"Me.BackColor = " & colorString)
End If
End Sub
Public Function ShouldAddProjectItem(ByVal filePath As String) As Boolean _
Implements IWizard.ShouldAddProjectItem
Return True
End Function
End Class
c#
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
using Microsoft.VisualStudio.TemplateWizard;
namespace WizardClassLibrary
{
public class MyWizard : IWizard
{
public void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
{
}
public void ProjectFinishedGenerating(EnvDTE.Project project)
{
}
public void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
{
}
public void RunFinished()
{
}
public void RunStarted(object automationObject, Dictionary<string, string>
replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
ColorPickerForm selector = new ColorPickerForm();
if (selector.ShowDialog() == DialogResult.OK)
{
Color c = selector.SelectedColor;
string colorString = "Color.FromArgb(" +
c.R.ToString() + "," +
c.G.ToString() + "," +
c.B.ToString() + ")";
extending Templates .
305
replacementsDictionary.Add
("this.BackColor = System.Drawing.Color.Silver",
"this.BackColor = " + colorString);
}
}
public bool ShouldAddProjectItem(string filePath)
{
return true;
}
}
}
In the RunStarted method, you prompt the user to select a new color and then use that response to
add a new entry into the replacements dictionary. In this case, you are replacing “Me.BackColor =
System.Drawing.Color.Silver” (VB) or “this.BackColor = System.Drawing.Color.Silver”
(C#) with a concatenated string made up of the RGB values of the color specified by the user. The
replacements dictionary is used when the files are created for the new project, because they will
be searched for the replacement keys. Upon any instances of these keys being found, they will be
replaced by the appropriate replacement values. In this case, you’re looking for the line specifying
that the BackColor is Silver, and replacing it with the new color supplied by the user.
The class library containing the implementation of the IWizard interface must contain a strongly
named assembly capable of being placed into the GAC. To ensure this, use the Signing tab of the
Project Properties dialog to generate a new signing key, as shown in Figure 15-12.
fiGure 15-12
After you check the Sign the Assembly checkbox, there will be no default value for the key file.
To create a new key, select <New . . .> from the drop-down list. Alternatively, you can use an
existing key file using the <Browse . . .> item in the drop-down list.
306 .
chaPter 15 projecT And iTem TemplATeS
Generating the extended Project template
You’re basing the template for this example on the ExtendedProjectTemplateExample project,
and you need only make minor changes in order for the wizard you just built to work correctly.
In the previous section you added an entry in the replacements dictionary, which searches for
instances where the BackColor is set to Silver. If you want the MainForm to have the BackColor
specified while using the wizard, you need to ensure that the replacement value is found.
To do this, simply set the BackColor property of the MainForm to Silver. This will add the line
“Me.BackColor = System.Drawing.Color.Silver” to the MainForm.Designer.vb file (VB) or
“this.BackColor = System.Drawing.Color.Silver” to the MainForm.Designer.cs file so that
it is found during the replacement phase.
Now you need to associate the wizard with the project template
so that it is called when creating a new project from this
template. Unfortunately this is a manual process, but you can
automate it once you’ve made these manual changes upon
subsequent rebuilds of the project. Start by exporting the
ExtendedProjectTemplateExample as a new project template as
per the previous instructions. Find the .zip file for this template
in Windows Explorer and unzip it. Take the .vstemplate
file and the icon file and put it into the folder containing the
ExtendedProjectTemplateExample project. The other files from
the unzipped template can be disregarded — you’ll note that
these are just the same files from the project folder that you will
be using in your template’s output instead, so you now have all
the files you need in the project folder. Make sure that you do not
include these files in the ExtendedProjectTemplateExample itself;
they should appear as excluded files, as shown in Figure 15-13.
You will notice the .zip file in the WizardClassLibrary project — this is the template file that
Visual Studio exported (which you want compiled into the setup project). For the moment, take
the project template .zip file that Visual Studio created and copy it into the WizardClassLibrary
project folder. Show all files for the project (as per Figure 15-13), right-click the file, and select
Include In Project. In the Properties window, set its Build Action property to Content. This is for
the installer you set up earlier — it will include the Content files from the class library in the
setup file, and these will be placed in the Visual Studio Templates folder as part of the installation
process.
To have the wizard triggered when you create a project from this template, add some additional
lines to the MyTemplate.vstemplate file:
<VSTemplate Version=“2.0.0”
xmlns=“http://schemas.microsoft.com/developer/vstemplate/2005” Type=“Project”>
<TemplateData>
...
</TemplateData>
fiGure 15-13
extending Templates .
307
<TemplateContent>
...
</TemplateContent>
<WizardExtension>
<Assembly>WizardClassLibrary, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=022e960e5582ca43, Custom=null</Assembly>
<FullClassName>WizardClassLibrary.MyWizard</FullClassName>
</WizardExtension>
</VSTemplate>
The <WizardExtension> node added in the sample indicates the class name of the wizard and
the strong-named assembly in which it resides. You have already signed the wizard assembly,
so all you need to do is determine the PublicKeyToken by opening the assembly using Lutz
Roeder’s Reflector for .NET (available at
http://www.red-gate.com/products/
reflector/). If you haven’t already built
the WizardLibrary you will have to build
the project so you have an assembly to open
with Reflector. Once you have opened the
assembly in Reflector (go to File . Open
and select the assembly) you can see the
PublicKeyToken of the assembly by selecting
it in the tree, as shown in Figure 15-14. The
PublicKeyToken value in the .vstemplate
file needs to be replaced with this value you
found using Reflector.
The last change you need to make to the ExtendedProjectTemplateExample is to add