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

Working with Project Template and Wizard Extension

By , 29 May 2012
 

Introduction

Project Templates and Item Templates are hidden gems of Development in Visual Studio. It not only improves development time but also forces any architectural concept specially in large projects.

This is not a first attempt to write any article on Project Template but I am trying to address what ever I developed in my recent work. Going forward I may extend this article for future aspects.

Background

Project Templates and Item Templates are providing common project structure to enterprise solution. Project Templates are very useful when you have series of similar project in one/many solution(s). It provides common project references, class structure, default implementation and lot more.

Get more information about Project and Item templates @ http://msdn.microsoft.com/en-us/library/6db0hwky.


Using the code

In this article, we will create a customize Project Template with Wizard Extension. You will get more on project template at MSDN site http://msdn.microsoft.com/en-us/library/s365byhx.aspx. We will use Wizard Extension to get inputs from user at time of creating project.

In our solution we will assume that all projects are implementing MyProjectTemplate.BaseProject class and IDisposable interface;



using MyProjectTemplate.BaseProject;
namespace MyExternalService
{
    public class ExternalService : BaseService, IDisposable
    {
    }
}
 


Create a Base Project Template

To create a Project Template, we require a base project template, where we are referring to external references, a customize Class, customize external files (will input files in wizard). One can extend this to various number of combinations like Config files, Namespaces etc...

We can modify this project and their class by using project template attributes.

Create a Project

Create a project and add all required references including MyProjectTemplate.DLL.

You also need to add Request.XML and Response.XML into resources folder.

Add default Class

Now add one class MyExternalService to your project and implement it for BaseService and IDisposable

using MyProjectTemplate.BaseProject;
 
namespace MyExternalService
{
    public class MyExternalService : BaseService, IDisposable
    {
 
        public override string Action
        {
            get { return "POST"; }
        }
 
        public override bool ValidateRequest(object context, object request)
        {
            if (!base.ValidateRequest(context, request)) return false;
 
            //Do customized request validation

            return true;
        }
 
        public override int SLA
        {
            get { return 30; }
        }
 
        public override int Timeout
        {
            get { return 20; }
        }
 
        protected override string RequestXML
        {
            get { return "MyExternalService.Resources.Request.XML"; }
        }
 
        protected override string ResponseXML
        {
            get { return "MyExternalService.Resources.Response.XML"; }
        }
 
        protected override string TargetURL
        {
            get { return "http://localhost/MyExternalService"; }
            // do the default request header mapping using requestHeader
        }
 
        public void Dispose()
        {
            //Release local XML file
        }
    }
}  

Make sure that you are able to compile project so that when we will create template, our created project should not introduce any errors.

 

Substitute Parameters in Project

Now our project is ready to substitute parameters.

In above class replace namespace and class name MyExternalService by $projectname$. You can also replace $projectname$ in RequestXML and ResponseXML property so new property should refer to XML files in newly created project.

After substituting parameters in above class our project will look like below.



using MyProjectTemplate.BaseProject;
namespace $projectname$
{
    public class $projectname$ : BaseService, IDisposable
    {
 
        public override string Action
        {
            get { return "POST"; }
        }
 
        public override bool ValidateRequest(object context, object request)
        {
            if (!base.ValidateRequest(context, request)) return false;
 
            //Do customized request validation

            return true;
        }
 
        public override int SLA
        {
            get { return 30; }
        }
 
        public override int Timeout
        {
            get { return 20; }
        }
 
        protected override string RequestXML
        {
            get { return "$projectname$.Resources.Request.XML"; }
        }
 
        protected override string ResponseXML
        {
            get { return "$projectname$.Resources.Response.XML"; }
        }
 
        protected override string TargetURL
        {
            get { return "http://localhost/MyExternalService"; }
            // do the default request header mapping using requestHeader
        }
 
        public void Dispose()
        {
            //Release local XML file
        }
    }
} 




Note: Don't worry now you will not able to compile your project but that should be fine for now.

Export Project Template

Now select this project and click on Export Template from File menu in your Visual Studio, an Export Template Wizard should appear.

  • Select project from drop-down in Export Template Wizard and click next.


  • Provide Template name, description, icon and default preview image and click on Finish button.

After exporting a project as Project Template now your exported project should appear in Project Template lists in Add/Create project list in Visual Studio.

This project template is still a basic and not taking any inputs from user like XML Files, URL, timeouts etc...

To implement more customization in Project Template you need to use Wizard Extension, where you will create a form to enter inputs from user and eventually it will be use in Creating a New project using project template.

You can find detailed implementation @ Visual Studio 2005 Project and Item Templates.

 

Create a Wizard Extension

As described in earlier section, to add User Inputs at time of creating new project, we will add Wizard Extension, which uses an input form.

To create a Wizard Extension we have to add one Class Library project. In this exercise we have to perform below mandatory functionality. 

  • Add a class inheriting from IWizard interface, which is part of Microsoft.VisualStudio.TemplateWizard namespace.
  • Add a windows form and provide input fields like Open File Dialog, text box and Submit button.
  • Create a strong type Assembly with a strong key and register into GAC.

Add a Property Class to store input from user.

Add a ProjectProperty class with get/set property only (DTO), this class will help to store and transfer user inputs from Form. You can customize this class for your own needs.

    public class ProjectProperty
    {
        public string Method = "POST";
        public string RequestSchemaFilePath = "";
        public string RequestSchemaFile = "";
        public string ResponseSchemaFilePath = "";
        public string ResponseSchemaFile = "";
        public int SLA = 20;
        public int TimeOut = 15;
        public string ExactTargetURL = @"https://MyWebservice.com/AddCustomer.asmx";
    }   


Add a Windows Form with all customized inputs.



Add a Windows Form with all customized controls and exposed one property of type ProjectProperty. So when user Submit the form Wizard can take control values from exposed property.

        public ProjectProperty ESInterfaceProject
        {
            get { return this.projectProperty; }
        }
 

        private void btnSubmit_Click(object sender, EventArgs e)
        {
            int.TryParse(txtSLA.Text, out this.projectProperty.SLA);
            int.TryParse(txtTimeout.Text, out this.projectProperty.TimeOut);
            this.projectProperty.ExactTargetURL = txtTargetURL.Text;
            this.projectProperty.Method = txtMethod.Text;
            this.Close();
        } 

Implement IWizard interface.

Add a class with implementing IWizard interface.

namespace MyProjectTemplate
{
    public class MyCustomWizard : IWizard
    {
    }
}  

IWizard implementing RunStarted method, show your windows form from this method and map all customize values (ProjectProperty) to the replacementsDictionary collection.

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            try
            {
                // Display a form to the user. The form collects 
                // input for the custom message.
                inputForm = new SchemaForm();
                inputForm.ShowDialog();
 
                //customMessage = inputForm.get_CustomMessage();

                // Add custom parameters.
                replacementsDictionary.Add("$path1$",
                    inputForm.ESInterfaceProject.RequestSchemaFilePath);
                replacementsDictionary.Add("$path2$",
                    inputForm.ESInterfaceProject.ResponseSchemaFilePath);
                replacementsDictionary.Add("$file1$",
                    inputForm.ESInterfaceProject.RequestSchemaFile);
                replacementsDictionary.Add("$file2$",
                    inputForm.ESInterfaceProject.ResponseSchemaFile);
                replacementsDictionary.Add("$method$",
                    inputForm.ESInterfaceProject.Method);
                replacementsDictionary.Add("$sla$",
                    inputForm.ESInterfaceProject.SLA.ToString());
                replacementsDictionary.Add("$timeout$",
                    inputForm.ESInterfaceProject.TimeOut.ToString());
                replacementsDictionary.Add("$targeturl$",
                    inputForm.ESInterfaceProject.ExactTargetURL);
 
                EnvDTE.DTE dte = automationObject as EnvDTE.DTE;
 
                string solutionPath = System.IO.Path.GetDirectoryName(dte.DTE.Solution.FullName);
                string projectPath = System.IO.Path.GetDirectoryName(dte.DTE.FullName);
 
                replacementsDictionary.TryGetValue("$projectname$", out projectPath);
 
                projectPath = solutionPath + "\\" + projectPath;
 
                string xsdPath = projectPath + @"\Resources";
 
                replacementsDictionary.Add("$xsdpath$",
                    xsdPath);
 
                this._xsdPath = xsdPath;
 
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        } 

Adding Files to Project

You can not add files to project structure in RunStarted method, because at time of RunStarted method executing, project structure is been creating in Temp folder and DTE is trying to read files from Temp folder which are not existing.

Other problem is you can't create any file and folder in project directory because at time of creating file/folder in project folder IO will create project folder and after RunStarted method DTE will give error because it will already have project folder, and it assuming that project has been already created.

So the final solution is, you need to copy files after project has been creating, which you can achieve by implementing RunFinished method, which executes when DTE completing project creating task.

 public void RunFinished()
        {
            // To copy a folder's contents to a new location:
            // Create a new target folder, if necessary.
            if (!System.IO.Directory.Exists(_xsdPath))
            {
                System.IO.Directory.CreateDirectory(_xsdPath);
            }
            // copy files
            System.IO.File.Copy(inputForm.ESInterfaceProject.RequestSchemaFilePath, _xsdPath + "\\" + inputForm.ESInterfaceProject.RequestSchemaFile, true);
            System.IO.File.Copy(inputForm.ESInterfaceProject.ResponseSchemaFilePath, _xsdPath + "\\" + inputForm.ESInterfaceProject.ResponseSchemaFile, true);
        }
 

Register in GAC

After compiling above DLL with Strong Key, register it in GAC. This is a mandatory step as project template has access of GAC.

 

Updating Project Template Again 

Now we are ready with our customized Wizard Extension using IWizard interface. In this section we will add substitute parameters in our project.

After updating parameters in project template class, it will look like below. 

using MyProjectTemplate.BaseProject;
namespace $projectname$
{
    public class $projectname$  : BaseService, IDisposable
    {
 
        public override string Action
        {
            get { return "$method$"; }
        }
 
        public override bool ValidateRequest(object context, object request)
        {
            if (!base.ValidateRequest(context, request)) return false;
 
            //Do customized request validation

            return true;
        }
 
        public override int SLA
        {
            get { return $sla$; }
        }
 
        public override int Timeout
        {
            get { return $timeout$; }
        }
 
        protected override string RequestXML
        {
            get { return "$projectname$.Resources.$file1$"; }
        }
 
        protected override string ResponseXML
        {
            get { return "$projectname$.Resources.$file2$"; }
        }
 
        protected override string TargetURL
        {
            get { return "$targeturl$"; }
            // do the default request header mapping using requestHeader
        }
 
        public void Dispose()
        {
            //Release local XML file
        }
    }
} 

Export Project 

Now again export above project in same way we exported in earlier section. 

Updating .vstemplate File  

Now update .vstemplate file and update with Wizard Extension.

Open .vstemplate file in notepad and add below code and save it. 

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
  <TemplateData>
    ...
  </TemplateData>
  <TemplateContent>
    ...
  </TemplateContent>
  <WizardExtension>
    <Assembly>MyProjectTemplate, Version=1.0.0.1, Culture=Neutral, PublicKeyToken=27092cf962afb8d7</Assembly>
    <FullClassName>MyProjectTemplate.MyCustomWizard</FullClassName>
  </WizardExtension>

</VSTemplate>    

Zip it and Save it in My Exported Templates location 

Now Zip entire project template and .csprj files from "My Exported Templates" location. 

Finally 

We are almost ready with our customized Project Template with Wizard Extension. If everything is in place then you will able to see Project Template in Add/Create project menu in Visual Studio.

When you will create a new project you will prompt for Files and other inputs from user, which information being used to generate class and project. You might need to include Resources folder into project files manually (I am working on this part to create it automatically and will update you once I am done).  

This way you can customize N number of combinations and you can also use in Config files, XML files etc... 

Code : Coming Soon.... I will update code in couple of days. 

License

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

About the Author

Joshi, Rushikesh
Software Developer (Senior) Synechron
United States United States
Member
Having 10 Years of Software Development expierence.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCodememberFireBall172529 Jan '13 - 2:29 
Can you provide the full source code for this example?
 
Thanks
GeneralMy vote of 4mvpKanasz Robert5 Nov '12 - 2:47 
not bad
QuestionWill this work for ItemTemplate?memberguru.sarkar21 Sep '12 - 6:03 
Rushikesh,
 
This is a very nice article. I want to do something similar with an ItemTemplate. Is it possible to use customization ItemTemplate with Wizards? If so can you please share an example if you have.
The moment I add WizardExtensions to my custom ItemTemplate, it stops showing in the Add New Item window.
 
Thanks.
/Guru

GeneralMy vote of 5memberDishR30 May '12 - 19:04 
Please provide code

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 30 May 2012
Article Copyright 2012 by Joshi, Rushikesh
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid