Click here to Skip to main content
Click here to Skip to main content

Create Item Templates Which Have Nested Items

By , 5 May 2008
Rate this:
Please Sign up or sign in to vote.

Introduction

Many of has have wondered how Visual Studio implements nesting of items in the solution explorer of items. In case I am not explaining myself well, I am talking about the nested appearance of the three items that represent a Form object:

  • Form1.cs
  • Form.Designer.cs
  • Form.resx

The post from my blog can be found here.

Background

Visual Studio 2005 does not have the functionality to choose several items and create a nesting schema. In order to do this, you must open the *.csproj file with your favorite editor and then do some stuff as described here.

I would expect Visual Studio 2008 to have implemented this feature, but it doesn't. So what can you do? One solution is to use the macro from delarou's blog. The idea is fairly simple but it was the only article I found that explained the know-how of how to tell Visual Studio programmatically to nest items. I couldn't have done without this post.

But macros were not my interest because usually nested items are a product of an insertion of a templated item. So in this article I will focus on how to achieve this when creating a custom template item with multiple items. For example, a type dataset.

An article about how to create a Custom Item Template can be found here.

I will get into depth with this process because there are many articles about this on the Internet. The only thing that I have to say is that the Custom Item Template file (MyTemplate.vstemplate) that describes the template and files to use, does not allow the <DependentUpon> attribute on each item in it.

So the only solution is to attach to the template, custom logic which is implemented in a class which implements the Microsoft.VisualStudio.TemplateWizard.IWizard interface and tell the MyTemplate.vstemplate to use the assembly that contains the file.

The only problem for this solution is that all template logic related assemblies must be located in gac so the related projects must be signed.

The Infrastructure

After much hard testing, I came up with a solution which requires from a class only to define the logic that separate nested items from their parent. This is achieved because template logic is defined in a class that inherits from Sarafian.Templates.NestedItemTemplateBase.Base which implements the IWizard interface.

This class stored privately which project is the parent and which are its children. While items are added to a project and sent to the template logic to be consumed, the template in the ProjectItemFinishedGenerating functions calls the abstract function GetProjectItemType and decides if it is a parent or nested.

public void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
{
    ProjectItemTypes type = GetProjectItemType(projectItem);
    switch (type)
    {
        case ProjectItemTypes.Parent:
            this.parentProjectItem = projectItem; 
            break;
        case ProjectItemTypes.Child:
            this.childrenProjectItems.Add(projectItem);
            break;
    }
}

Then when the template is finished, it adds the nested items to its parent like this:

public void RunFinished()
{
    foreach (EnvDTE.ProjectItem item in this.childrenProjectItems)
    {
    string filename = item.get_FileNames(0);
    this.parentProjectItem.ProjectItems.AddFromFile(filename);
    }
}

Because the project is required to be placed in the gac, this post-build event is defined:

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" 
    /if "$(TargetPath)"

Creating the Template

In the zip file above, I have implemented an example.

The template item I want to create consists of four files:

  • Example.cs
  • Example.Input.cs
  • Example.Output.cs
  • Example.Error.cs

Example.cs will be the parent and all others its children. So this is how it is done:

protected override ProjectItemTypes GetProjectItemType(EnvDTE.ProjectItem item)
{
    if (item.Name.IndexOf(".Input.cs") > 0)
    {
        return ProjectItemTypes.Child;
    }
    if (item.Name.IndexOf(".Output.cs") > 0)
    {
        return ProjectItemTypes.Child;
    }
    if (item.Name.IndexOf(".Error.cs") > 0)
    {
        return ProjectItemTypes.Child;
    }

    return ProjectItemTypes.Parent;

} 

It doesn't matter if they are totally different classes or partial. For practical reasons, the entire collection of files needed for the template zip file are placed in a directory called ExampleItem and are marked as Compile none and Copy if Newer in the properties of each one.

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" 
    /if "$(TargetPath)"
"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" 
    /l "$(TargetName)"

After the first compilation, a public key token is generated that must be set only once in the MyTemplate.vstemplate file like this:

<WizardExtension>
    <Assembly>Sarafian.Templates.ExampleItemTemplate, Version=1.0.0.0, 
        Culture=neutral, PublicKeyToken=6df23c9c2e2f12af</Assembly>
    <FullClassName>Sarafian.Templates.ExampleItemTemplate.Template</FullClassName>
</WizardExtension>

After this, the template is ready to be used.

Points of Interest

Some things to keep in mind. Because Visual Studio uses a cached version of the gac, any change made to the binary aspect of the template requires the restart of Visual Studio that will use the template.

Debug cannot be done with traditional techniques but old techniques were used in order to understand what happens within an IWizard implemented class. This is what I have understood.

First of all ShouldAddProjectItem is called. If false is returned, then the file is never templated. This means that no parameters are replaced within it. The filepath has the value of the original file at this point.

After the replacement of custom parameters, ProjectItemFinishedGenerating is called. At this point, the item's Name is the generated one. For example, if I wish my template to be called Sarafian, then for the Example.Input.cs in the template, the item name is Sarafian.Input.cs.

After all files have been processed by the above function, BeforeOpeningFile is called which tells Visual Studio to open for editing the file. In this case, the item always corresponds to Example.cs.

Finally RunFinished is called.

The above sequence is followed when the template is Item Template and not Project Template.

More info can be found in this article by Ron Petrusha which proved very helpful for me.

History

  • 25th January, 2008: Initial post
  • 5th May, 2008: Added Solution for Visual Studio 2008
    Future implementations will have a command line that will dynamically generate the zip file for the template. Interestingly, I cannot find the command to use Windows zip functionality. Any comments or directions would be much appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sarafian
Team Leader ALGOSYSTEMS
Greece Greece
I live in Athens Greece and currently I am working with Business scale application with .NET latest technologies
 
I've been developing applications for personal and friends usage with C++ using majorly Borland's various IDEs since 1994.
In 2002 I began working for an R&D institute where I was introduced to C# which I worships ever since.
 
I love core application development and I would like to publish more articles here and on my blog, but there is not enough time to do so.
 
I usualy "waste" my spare time watching sitcoms, preferable SCI-FI.
I would like to play chess but I can't find any real world players to hang out with.

Comments and Discussions

 
QuestionFails when there is more than one parent node PinmemberMelkin19-Oct-12 8:34 
AnswerRe: Fails when there is more than one parent node PinmemberSarafian19-Oct-12 11:47 
GeneralRe: Fails when there is more than one parent node PinmemberMelkin19-Oct-12 15:06 
GeneralRe: Fails when there is more than one parent node PinmemberSarafian20-Oct-12 0:14 
QuestionDon't want to use GAC. PinmemberMember 85448786-Jan-12 14:23 
AnswerRe: Don't want to use GAC. PinmemberSarafian6-Jan-12 22:11 
GeneralRe: Don't want to use GAC. PinmemberMember 85448787-Jan-12 9:25 
GeneralRe: Don't want to use GAC. PinmemberMember 793034125-May-12 2:27 
GeneralRe: Don't want to use GAC. PinmemberSarafian26-May-12 9:37 
GeneralRe: Don't want to use GAC. PinmemberMelkin19-Oct-12 6:23 
GeneralCan't get it to work... PinmemberArif.Khan1-May-08 15:21 
GeneralRe: Can't get it to work... PinmemberArif.Khan1-May-08 19:13 
GeneralRe: Can't get it to work... PinmemberSarafian1-May-08 23:35 
GeneralRe: Can't get it to work... PinmemberSarafian1-May-08 23:34 
GeneralRe: Can't get it to work... PinmemberSarafian5-May-08 2:46 
GeneralDownload Link PinmemberSarafian23-Apr-08 4:11 
GeneralSource code missing PinmemberTBermudez23-Apr-08 4:10 
GeneralRe: Source code missing PinmemberSarafian23-Apr-08 5:32 
GeneralRe: Source code missing PinmemberSarafian23-Apr-08 23:03 
GeneralA Comment PinmemberSarafian2-Apr-08 6:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 5 May 2008
Article Copyright 2008 by Sarafian
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid