|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Contents
IntroductionThis article describes how the code templates that are used when adding new project items in Visual Studio .NET work. There are several other articles on CodeProject that touches on this subject (a few are listed in the References section at the bottom) and the goal of this article is to describe in more detail how it works. A component, called Krevera Template Manager, is also introduced that makes it (fairly) easy to create custom templates and its functionality is described. Included with the component are templates for strongly typed collections and strongly typed dictionaries for C#, VB and C++. If needed, this component can be extended to create even more powerful templates. All code examples are written in C#. The component is available in two versions, one for Visual Studio .NET 2003 and one for Visual Studio .NET 2005. The latter has been tested on Beta 2 but will probably work with later versions as well. BackgroundThere are several types of templates and wizards in Visual Studio, most notably project templates and project item templates. Project templates contain the basic files needed for the different types of projects, and project item templates are single file templates (or multiple if the item has code-behind files) that can be included in projects. To be completely accurate, there is actually no need of actual template files for all project items since they can also be created purely on-the-fly, but it's normally easiest to have a template file for each project item and then adjust it slightly when it's included in the project. This article is about project item templates, for project templates the reader is advised to look elsewhere. When adding a new project item to a project, the Add New Item dialog is displayed:
This dialog is where we will add our custom templates. How does it work?The items in the dialog above are generated by Visual Studio by reading configuration files from the current project type's project item directory. For example, if the current project is a Visual Basic project, then the items in this directory: C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\VBProjectItems
are analyzed (that is on my system of course, the exact location depends on where the installation was made). There are two important types of files in the project item directory:
These two types of files and a COM component that creates the instance are all that are needed to create project items. Here's an overall picture of how everything is connected:
The format of these files will now be more closely described before we introduce our solution to create flexible project item templates (as you might have guessed, it's an implementation of a COM component as described above). Note that we can create categories of project items by storing our .vsdir and .vsz files in sub-directories. In the image above, we have sub-directories, for example, for "Local Project Items", "UI", "Code", etc. Project item directoriesBefore we go into the details of file formats, it might be useful to know where to find the files. As usual, there are, of course, several ways. Easiest is probably to use the Explorer to navigate to the install directory of Visual Studio and look for sub-directories for the different .NET languages. For example, VB7 obviously contains stuff for Visual Basic. Within that directory, look for a directory name with "projectitem" in it. Soon enough, we find the VBProjectItems directory which contains the files. It's equally easy to find the project item directories for other project types. However, if we want to do this programmatically, we use a more reliable way namely reading from the registry, under this key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\
Projects\{[PROJECT_TYPE_GUID]}
The value we're interested in, is called VSLangProj.PrjKind.prjKindCSharpProject
VSLangProj.PrjKind.prjKindVBProject
Unfortunately, Here's some code that retrieves the project item directory for C#: const string sVisualStudioVersion = "7.1";
RegistryKey rk;
// C#
rk = Registry.LocalMachine.OpenSubKey("SOFTWARE")
.OpenSubKey("Microsoft")
.OpenSubKey("VisualStudio")
.OpenSubKey(sVisualStudioVersion)
.OpenSubKey("Projects")
.OpenSubKey(VSLangProj.PrjKind.prjKindCSharpProject);
_sCSProjItemPath = (string)rk.GetValue(@"ItemTemplatesDir");
Note: For Visual Studio .NET 2005 we set .vsdir file formatA .vsdir file lists a set of items and their properties. There can be several .vsdir files in the same directory. The file format is described in MSDN (use the index search to find ".VSDir files") but here's a brief summary:
Here's an example of what a .vsdir file might look like (the lines are wrapped for readability): Strongly Typed Collection.vsz|0|Typed Collection|1|
Sub-class of CollectionBase with elements of a given type|
0|0|0|TypedCollection.cs
Strongly Typed Dictionary.vsz|0|Typed Dictionary|1|
Sub-class of DictionaryBase with keys and values of the selected types|
0|0|0|TypedDictionary.cs
If we store that file in a sub-directory "Krevera Template Manager", the following items will be displayed in the Add New Item dialog (a few column numbers are given in the image to show how the column strings of the .vsdir file are used):
When the user selects an item and clicks the Open button, then Visual Studio opens the item's .vsz file, so it's time to describe its format now. .vsz file formatThe sole purpose of .vsz files is to tell Visual Studio what component to call to create the project item and the parameters it needs. The file format is very simple and here's an example (namely the standard "Class" project item for C#): VSWIZARD 7.0
Wizard=VsWizard.VsWizardEngine.7.1
Param="WIZARD_NAME = CSharpAddClassWiz"
Param="WIZARD_UI = FALSE"
Param="PROJECT_TYPE = CSPROJ"
Description:
There is not really a whole lot more to say about the .vsz file format, so we continue with describing the logic of actually creating project items. COM componentAs already mentioned, the actual project item is created by a component pointed out by a .vsz file. The component must fulfill a few requirements:
The parameters to the
And that's it. When the project item is to be created, Visual Studio creates an instance of the class and calls its Microsoft has created a fairly large number of project item templates that work in this way. The actual template files are not stored together with the .vsdir and .vsz files, but separately. It's entirely possible to adjust them, using the method described in the Code Project article "Customizing Visual Studio's Code Generation Templates" but the possible modifications are limited to layout changes, standard file headers etc. No dynamic content (current date, username, etc.) can be added, since the component that adjusts the template file for the project is limited to do what the Visual Studio developers deemed feasible. Also there is, as far as I know, no documentation of the variable replacements that are performed by the standard component. Another problem is that, we cannot build new logic, for example asking the user for information. To allow creating more complex templates, we thus have to create a new COM Krevera Template ManagerKrevera Template Manager is basically a COM component for handling requests for creating new project items. It's written in C# and the full source code is available for download at the top of this article. There's also a binary download that contains an installation program for the component and a few sample templates for creating strongly typed collections and dictionaries in C#, VB and C++. Templates are normal text files that contain variables that Krevera Template Manager replaces when creating the project items. The variables are written in XML and include the following (the <variable type="standard" name= "[variablename]"/>
There are 13 different variables that can be used:
We can also access environment variables by setting the <variable type="environment" name="TEMP"/>
Finally, we can also use custom variables whose values we retrieve by asking the user in a wizard-like interface. Here's an example: <wizard>
<page>
<title>Select data type</title>
<description>Select data type for which to create
a strongly typed collection</description>
<variable name="datatype"/>
</page>
</wizard>
When creating the project item whose template contains the above wizard fragment, the following dialog is displayed:
When the user has typed the wanted value for the custom variable (named " <variable type="custom" name="datatype"/>
To make sure that the variable declaration for the template does not collide with the code syntax in the template text, all variables must be surrounded by To summarize, we now show the top part of a real template, namely the template for creating strongly typed collections that is distributed with the code: [%
<wizard>
<page>
<title>Select data type</title>
<description>Select data type for which to create a
strongly typed collection</description>
<variable name="datatype"/>
</page>
</wizard>
%]using System;
using System.Collections;
namespace [%<variable name="namespace"/>%] {
public class [%<variable type="standard"
name="filenamebase"/>%] : CollectionBase
{
public [%<variable type="custom"
name="datatype"/>%] this[ int index ] {
get
{
return( ([%<variable type="custom"
name="datatype"/>%]) List[index] );
}
set
{
List[index] = value;
}
}
...
If we insert a project item of this type into a project with a default namespace of using System;
using System.Collections;
namespace CS_Test
{
public class FoobarCollection : CollectionBase
{
public Foobar this[ int index ]
{
get
{
return( (Foobar) List[index] );
}
set
{
List[index] = value;
}
}
...
Hopefully everyone agrees that, this is a very easy way of creating strongly typed collections, compared to doing it yourself. Other frequent uses for templates are to enforce organization-wide rules about file headers. With this component, it's easy to include filename, creation date, username, etc. automatically in the header of the files. It's also easy to create new variables if needed, just modify the Implementation overviewThe code will not be described in detail here; it's probably easier to read the code and its comments directly. It may however be useful to have an overview of how the code works, so here we go:
InstallationEither run the installation program (and restart Visual Studio if it's open during the installation) or do it manually:
Those steps should be enough to use the templates. LicenseYou're allowed to use this software in any way you want, except for making money. You're free to use it privately or in your organization (in original or modified state) but you're not allowed to include it in commercial products etc. At least not without explicit approval by the author (i.e. me!). ReferencesBook:
Other Code Project articles:
History
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||