Creating Item Templates as Visual Studio 2010 Extensions

Posted by maziar on Geeks with Blogs See other posts from Geeks with Blogs or by maziar
Published on Tue, 03 Jul 2012 19:13:04 GMT Indexed on 2012/07/03 21:16 UTC
Read the original article Hit count: 375

Filed under:

This blog post briefly introduces creation of an item template as a Visual studio 2010 extension.

Problem specification

Assume you are writing a Framework for data-oriented applications and you decide to include all your application messages in a SQL server database table. After creating the table, your create a class in your framework for getting messages with a string key specified.

 

var message = FrameworkMessages.Get("ChangesSavedSuccess");

 

Everyone would say this code is so error prone, because message keys are not strong-typed, and might create errors in application that are not caught in tests. So we think of a way to make it strong-typed, i.e. create a class to use it like this:

 

var message = Messages.ChangesSavedSuccess;

in Messages class the code looks like this:

public string ChangesSavedSuccess
{
    get { return FrameworkMessages.Get("ChangesSavedSuccess"); }
}

 

And clearly, we are not going to create the Messages class manually; we need a class generator for it.

 

Again assume that the application(s) that intend to use our framework, contain multiple sub-systems. So each sub-system need to have it’s own strong-typed message class that call FrameworkMessages.Get method. So we would like to make our code generator an Item Template so that each developer would easily add the item to his project and no other works would be necessary.

 

Solution

We create a T4 Text Template to generate our strong typed class from database. Then create a Visual Studio Item Template with this generator and publish it.

 

What Are T4 Templates

You might be already familiar with T4 templates. If it’s so, you can skip this section.

T4 Text Template is a fine Visual Studio file type (.tt) that generates output text. This file is a mixture of text blocks and code logic (in C# or VB). For example, you can generate HTML files, C# classes, Resource files and etc with use of a T4 template.

 

Syntax highlighting

In Visual Studio 2010 a T4 Template by default will no be syntax highlighted and no auto-complete is supported for it. But there is a fine visual studio extension named ‘Visual T4’ which can be downloaded free from VisualStudioGallery. This tool offers IntelliSense, syntax coloring, validation, transformation preview and more for T4 templates.

 

 

How Item Templates work in Visual Studio

Visual studio extensions allow us to add some functionalities to visual studio. In our case we need to create a .vsix file that adds a template to visual studio item templates.

Item templates are zip files containing the template file and a meta-data file with .vstemplate extension. This .vstemplate file is an XML file that provides some information about the template.

A .vsix file also is a zip file (renamed to .vsix) that are open with visual studio extension installer. (Re-installing a vsix file requires that old one to be uninstalled from VS: Tools > Extension Manager.) Installing a vsix will need Visual Studio to be closed and re-opened to take effect. Visual studio extension installer will easily find the item template’s zip file and copy it to visual studio’s template items folder. You can find other visual studio templates in [<VS Install Path>\Common7\IDE\ItemTemplates] and you can edit them; but be very careful with VS default templates.

 

How Can I Create a VSIX file

1. Visual Studio SDK

depending on your Visual Studio’s version, you need to download Microsoft Visual Studio SDK. Note that if you have VS 2010 SP1, you will need to download and install VS 2010 SP1 SDK; VS 2010 SDK will not be installed (unless you change registry value that indicated your service pack number).

Here is the link for VS 2010 SP1 SDK.

After downloading, Run it and follow the wizard to complete the installation.

 

2. Create the file you want to make it an Item Template

Create a project (or use an existing one) and add you file, edit it to make it work fine.

 

Back to our own problem, we need to create a T4 (.tt) template.

VS-Prok: Add > New Item > General > Text Template

Type a file name, ex. Message.tt, and press Add.

Create the T4 template file (in this blog I do not intend to include T4 syntaxes so I just write down the code which is clear enough for you to understand)

 

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Data" #>
<#@ Import Namespace="System.Data.SqlClient" #>
<#@ Import Namespace="System.Text" #>
<#@ Import Namespace="System.IO" #>
<#
    var connectionString = "server=Maziar-PC; Database=MyDatabase; Integrated Security=True";
    var systemName = "Sys1";


    var builder = new StringBuilder();
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        var command = connection.CreateCommand();
        command.CommandText = string.Format("SELECT [Key] FROM [Message] WHERE System = '{0}'", systemName);
        var reader = command.ExecuteReader();
        while (reader.Read())
        {
            builder.AppendFormat("        public static string {0} {{ get {{ return FrameworkMessages.Get(\"{0}\"); }} }}\r\n", reader[0]);
        }
    }
#>

namespace <#= System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") #>
{
    public static class <#= Path.GetFileNameWithoutExtension(Host.TemplateFile) #>
    {
<#= builder.ToString() #>
    }
}

As you can see the T4 template connects to a database, reads message keys and generates a class. Here is the output:

namespace MyProject.MyFolder
{
    public static class Messages
    {
        public static string ChangesSavedSuccess { get { return FrameworkMessages.Get("ChangesSavedSuccess"); } }
        public static string ErrorSavingChanges { get { return FrameworkMessages.Get("ErrorSavingChanges"); } }

    }
}

 

The output looks fine but there is one problem. The connectionString and systemName are hard coded. so how can I create an flexible item template? One of features of item templates in visual studio is that you can create a designer wizard for your item template, so I can get connection information and system name there. now lets go on creating the vsix file.

 

3. Create Template

In visual studio click on File > Export Template

a wizard will show up.

if first step click on Item Template on in the combo box select the project that contains Messages.tt. click next.

Select Messages.tt from list in second step. click next.

In the third step, you should choose References. For this template, System and System.Data are needed so choose them. click next.

write down template name, description, if you like add a .ico file as the icon file and also preview image. Uncheck automatically add the templare … . Copy the output location in clip board. click finish.

 

 

4. Create VSIX Project

In VS, Click File > New > Project > Extensibility > VSIX Project

Type a name, ex. FrameworkMessages, Location, etc.

The project will include a .vsixmanifest file.

Fill in fields like Author, Product Name, Description, etc.

 

In Content section, click on Add Content.

choose content type as Item Template. choose source as file.

remember you have the template file address in clipboard? now paste it in front of file. click OK.

 

 

5. Build VSIX Project

That’s it, build the project and go to output directory. You see a .vsix file. you can run it now.

After restarting VS, if you click on a project > Add > New Item, you will see your item in list and you can add it. When you add this item to a project, if it has not references to System and System.Data they will be added.

but the problem mentioned in step 2 is seen now.

 

 

6. Create Design Wizard for your Item Template

Create a project i.e. Windows Application named ‘Framework.Messages.Design’, but better change its output type to Class Library.

Add References to Microsoft.VisualStudio.TemplateWizardInterface and envdte

Add a class Named MessagesDesigner in your project and Implement IWizard interface in it.

This is what you should write:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TemplateWizard;
using EnvDTE;

namespace Framework.Messages.Design
{
    class MessageDesigner : IWizard
    {
        private bool CanAddProjectItem;

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            // Prompt user for Connection String and System Name in a Windows form (ShowDialog) here
            // (try to provide good interface)
            // if user clicks on cancel of your windows form return;
            string connectionString = "connection;string"; // Set value from the form
            string systemName = "system;name"; // Set value from the form
            CanAddProjectItem = true;
            replacementsDictionary.Add("$connectionString$", connectionString);
            replacementsDictionary.Add("$systemName$", systemName);
        }

        public bool ShouldAddProjectItem(string filePath)
        {
            return CanAddProjectItem;
        }

        public void BeforeOpeningFile(ProjectItem projectItem)
        {
        }

        public void ProjectFinishedGenerating(Project project)
        {
        }

        public void ProjectItemFinishedGenerating(ProjectItem projectItem)
        {
        }

        public void RunFinished()
        {
        }
    }
}

 

before your code runs  replacementsDictionary contains list of default template parameters. After that, two other parameters are added.

Now build this project and copy the output assembly to [<VS Install Path>\Common7\IDE] folder.

 

your designer is ready.

 

 

The template that you had created is now added to your VSIX project. In windows explorer open your template zip file (extract it somewhere).

open the .vstemplate file.

first of all remove

<ProjectItem SubType="Code" TargetFileName="$fileinputname$.cs" ReplaceParameters="true">Messages.cs</ProjectItem>

because the .cs file is not to be intended to be a part of template and it will be generated.

change value of ReplaceParameters for your .tt file to true to enable parameter replacement in this file.

now right after </TemplateContent> end element, write this:

<WizardExtension>
  <Assembly>Framework.Messages.Design</Assembly>
  <FullClassName>Framework.Messages.Design.MessageDesigner</FullClassName>
</WizardExtension>

 

one other thing that you should do is to edit your .tt file and remove your .cs file.

Lines 8 and 9 of your .tt file should be:

    var connectionString = "$connectionString$";
    var systemName = "$systemName$";

this parameters will be replaced when the item is added to a project.

Save the contents to a zip file with same file name and replace the original file.

 

now again build your VSIX project, uninstall your extension. close VS.

now run .vsix file.

open vs, add an item of type messages to your project, bingo, your wizard form will show up. fill the fields and click ok, values are replaced in .tt file added.

 

 

that’s it. tried so hard to make this post brief, hope it was not so long…

 

Cheers

Maziar

© Geeks with Blogs or respective owner