基本上,我们每次在VS中新建一个项目(Project)或项(Item)的时候,都用到了它的模板。这些模板给我们提供了一个好的开始。如果你曾经历过从ASP.NET Web Application到AJAX Web Application的升级,就会对此有所体会;如果你是一个开源爱好者,需要在项目中应用大量的开源组件,那么每次的添加引用和配置也会让人厌烦。模板给我们节省了很多时间,并能使同类型的项目/项保持一致。
在VS中我们可以很方便地导出项目/项模板,可以参考下面两篇文章:
在VS2005中创建项目模板来提高开发效率
制作Visual Studio项目模板
本文将介绍模板的更多内容。
什么是项目/项模板
项目模板包含了创建特定类型的项目所需的必要文件(包括引用)。VS本身就提供了很多内置的项目模板,如Console Application, Class Library和ASP.NET Web Application等等。大部分项目模板基于一种特定的语言,如C#、F#等,同时针对特定的领域,如Windows、Web和Test等等,它们正是以此进行分类组织:
这里,创建一个控制台应用程序,
基本的引用已经添加完毕,并且有了一个Program.cs文件,接下来就可以在Main函数编写代码了。
项模板用于创建逻辑上的单个文件(有时可能是创建了多个文件,比如Web Form模板,不过它们在逻辑上仍被看作单个文件)。这个过程就不再赘述了。
项目/项模板简析
现在该了解一下这些模板是如何实现的了。前面提到,VS内置了很多模板,那就看看它们是怎么做的。
先来看项目模板,内置的项目模板位于[VS2008 Path]/Common7/IDE/ProjectTemplates,这里可以看到几个以语言名称命名的文件夹,进入CSharp,则是Windows、Web、Test等文件夹,这是前面提到的模板组织方式。继续下去,打开Windows/1033文件夹,可以看多几个zip文件包。从文件名可以想到,VS当中看到的模板就是这里的一个zip包。
事实正是如此。解压缩ConsoleApplication.zip文件,这里有四个文件:
.cs和.csproj文件就是创建项目后得到的文件,那vstemplate文件呢?它是模板的组织者,没有它,zip包就只是几个零散的文件而已。这个文件称为模板清单(Template Manifest),它实际上就是一个XML文件。
项模板的情况与此类似,它们位于[VS2008 Path]/Common7/IDE/ItemTemplates。
模板清单(Template Manifest)
清单文件是XML文件,它的Schema文件是[VS2008 Path]/XML/Schemas/1033/vstemplate.xsd,通过xsd文件我们可以了解清单文件的结构。
它的根节点是<VSTemplate>,该节点有两个Attribute:
- Type:指定模板类型(项目模板还是项模板),取值可以是Project、ProjectGroup或Item;
- Version:模板所用于的.NET Framework版本号。
该节点在Class文件的模板中是这样的:
<VSTemplate Version="3.0.0" Type="Item" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
</VSTemplate>
<VSTemplate>节点可以有四种子节点:
- <TemplateData>:必选项,包含了模板的基本信息,如名称、图标、默认名称等。
- <TemplateContent>:必选项,指定了模板所包含的文件。
- <WizardData>:可选项,包含了传给自定义向导的XML数据。
- <WizardExtension>:可选项,包含了一些自定义模板向导的信息。
由于这些节点包含的信息比较多,这里只好偷懒了,它们在MSDN的链接分别是:
另外还可以看看Visual Studio Templates主页,项目模板和项模板之间的区别。
有了上面这些知识,就可以对清单文件有全面的认识了,下面来看看如何创建模板。
创建模板
项目模板和项模板的创建有所不同,但是都有两种方式:自动方式和手工方式。使用自动方式,跟随VS的向导创建模板,而使用手工方式,则需要编写一些代码,尤其是前面提到的清单文件。一般情况下,自动方式更为简单、快捷,而手工方式则更为强大和灵活。
手工方式创建模板
其过程包含三个步骤:
- 创建模板所包含的文件;
- 创建清单文件,编写代码对模板进行配置;
- 部署。
首先创建一个类库项目,它的结构如下所示:
接下来在MyClassLibTemplate.vstemplate文件内对模板进行配置:
XML Code - 第一个模板清单文件
<VSTemplate Version="2.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
<TemplateData>
<Name>My ClassLib Template</Name>
<Description>A simple class library template</Description>
<ProjectType>CSharp</ProjectType>
<ProjectSubType></ProjectSubType>
<SortOrder>1000</SortOrder>
<CreateNewFolder>true</CreateNewFolder>
<DefaultName>MyClassLibProj</DefaultName>
<ProvideDefaultName>true</ProvideDefaultName>
<LocationField>Enabled</LocationField>
<EnableLocationBrowseButton>true</EnableLocationBrowseButton>
<Icon>ufo.ico</Icon>
</TemplateData>
<TemplateContent>
<Project TargetFileName="MyClassLib.csproj" File="MyClassLib.csproj"
ReplaceParameters="true">
<ProjectItem TargetFileName="SampleClass.cs" ReplaceParameters="true" >
SampleClass.cs
</ProjectItem>
<Folder Name="Properties" TargetFolderName="Properties">
<ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">
AssemblyInfo.cs
</ProjectItem>
</Folder>
</Project>
</TemplateContent>
</VSTemplate>
最后是部署。将所有这些文件放在一起(如果有文件夹,要保持它的层次结构),打包为zip文件。将文件拷贝到下面两个路径之一:
- 如果是项目模板,拷到[My Documents Path]/Visual Studio 2008/Templates/ProjectTemplates;
- 如果是项模板,拷到[My Documents Path]/Visual Studio 2008/Templates/ItemTemplates。
由于刚才创建的模板是项目模板,所以放在第一个路径下。然后打开VS 2008,新建一个项目,在My Templates下可以看到我们自定义的模板:
关于以自动(向导)方式创建模板,可以参考本文开头提到的两篇文章。
自定义模板
前面的例子很简单,甚至是过于简单了。在真实的开发中,可能需要向模板传递一些参数。
首先,需要在文件中插入占位符,如$Author$,Author就是参数的名称。看下面的例子:
C# Code - 设置模板参数占位符
namespace ClassLibrary1
{
// Company: $Company$
// Created By: $Author$
public class SampleClass
{
}
}
接下来需要在模板清单文件中定义这些参数和它们的值,向<TemplateContent>节点中添加<CustomParameters>节点:
XML Code - 设置参数值
<CustomParameters>
<CustomParameter Name="$Company$" Value="my company" />
<CustomParameter Name="$Author$" Value="Anders Cui" />
</CustomParameters>
好了,现在重新部署这个模板,添加一个新项目,得到的文件是:
C# Code - 使用参数之后
namespace ClassLibrary1
{
// Company: my company
// Created By: Anders Cui
public class SampleClass
{
}
}
需要注意的是,相关ProjectItem的ReplaceParameters特性值要为“true”。现在,如果模板中有10个文件用到了$Author$,就能节省不少时间了,不过我还是不满足,能不能在运行时获取用户的输入呢?看看下一节关于向导的内容。
自定义模板向导
VS模板的一个增强特性就是自定义向导,这些向导可以在用户由模板创建项目/项的时候运行。向导的好处在于可以:
- 收集用户的输入信息;
- 向模板添加参数值;
- 向项目添加其它文件;
使用模板向导,可以访问DTE对象(Development Tools Extensibility,是Visual Studio自动化对象模型中的主对象),从而可以方便地操作项目。创建模板向导的基本步骤为:
- 创建一个程序集,它要实现IWizard接口;
- 将该程序集注册到GAC(Global Assembly Cache);
- 更新模板清单文件,使用该程序集。
这里将继续使用上面的例子,不过这次要从用户界面收集参数值,而不是把它们放在模板清单中。
先来实现IWizard接口,该接口有6个方法,这些方法会在项目模板生命周期的特定时刻执行,其中的部分方法仅适用于项模板,它们的详情请参考:IWizard成员。
当Visual Studio开始创建项目时,它立即调用RunStarted方法,因此该方法是收集用户输入信息的良好位置。它有四个参数:
- Object 参数,可强制转换为根 _DTE 对象,以使您能够自定义项目。
- Dictionary 参数,它包含模板中所有预定义参数的集合。
- WizardRunKind 参数,它包含有关所使用的模板种类的信息。
- Object 数组,它包含通过 Visual Studio 传递给向导的一组参数。
在本例中,将来自用户的输入添加到Dictionary参数中。好了,现在可以开始编码了,新建一个类库项目,名称为CustomTemplateWizard,新建一个Windows Form,用来接受用户输入,界面大体如下:
其代码为:
C# Code - 接受用户输入的窗体
namespace CustomTemplateWizard
{
public partial class InputForm : Form
{
private string company;
private string author;
public InputForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
company = txtCompany.Text;
author = txtAuthor.Text;
this.Dispose();
}
public string GetCompany()
{
return company;
}
public string GetAuthor()
{
return author;
}
}
}
下面的类WizardImpl实现了IWizard接口:
C# Code - 实现IWizard接口的类WizardImpl
namespace CustomTemplateWizard
{
public class WizardImpl : IWizard
{
string company = string.Empty;
string author = string.Empty;
InputForm inputForm;
#region IWizard Members
public void BeforeOpeningFile(ProjectItem projectItem)
{
}
public void ProjectFinishedGenerating(Project project)
{
}
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
{
}
public void RunFinished()
{
}
public void RunStarted(object automationObject,
Dictionary<string, string> replacementsDictionary,
WizardRunKind runKind, object[] customParams)
{
try
{
inputForm = new InputForm();
inputForm.ShowDialog();
company = inputForm.GetCompany();
author = inputForm.GetAuthor();
replacementsDictionary.Add("$Company$", company);
replacementsDictionary.Add("$Author$", author);
replacementsDictionary.Add("$CreatedOn$", DateTime.Now.ToShortDateString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public bool ShouldAddProjectItem(string filePath)
{
return true;
}
#endregion
}
}
然后,编译项目,为项目添加强命名,接着注册到GAC(Global Assembly Cache)。
接下来要修改模板清单文件,来使用新出炉的程序集,这要在</TemplateContent>之后添加:
XML Code - 添加对程序集的引用
<VSTemplate Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
<TemplateData>
<Name>MyClassLib</Name>
<Description>MyCustomClassLib</Description>
<ProjectType>CSharp</ProjectType>
<ProjectSubType>
</ProjectSubType>
<SortOrder>1000</SortOrder>
<CreateNewFolder>true</CreateNewFolder>
<DefaultName>MyClassLib</DefaultName>
<ProvideDefaultName>true</ProvideDefaultName>
<LocationField>Enabled</LocationField>
<EnableLocationBrowseButton>true</EnableLocationBrowseButton>
<Icon>__TemplateIcon.ico</Icon>
</TemplateData>
<TemplateContent>
<Project TargetFileName="MyClassLib.csproj" File="MyClassLib.csproj" ReplaceParameters="true">
<Folder Name="Properties" TargetFolderName="Properties">
<ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
</Folder>
<ProjectItem ReplaceParameters="true" TargetFileName="SampleClass.cs">SampleClass.cs</ProjectItem>
</Project>
</TemplateContent>
<WizardExtension>
<Assembly>
CustomTemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=294ee683ed83bb3d
</Assembly>
<FullClassName>CustomTemplateWizard.WizardImpl</FullClassName>
</WizardExtension>
</VSTemplate>
这些都完成后,跟前面一样,打包、部署,然后打开VS,新建一个项目,此时出现一个对话框,在这里输入信息:
点击确定,生成的文件中可以看到参数已被替换:
初学者工具包(Starter Kits)
初学者工具包是一种特殊的项目模板,用于与其他开发人员分享示例代码和代码资源,可以看作是加强版的项目模板。初学者工具包往往包含了项目模板的所有内容,还可能包含额外的文档内容。
到些为止,项目模板的创建已经完成。但是还有一点麻烦的是,每次我们要安装此模板时,需要手工将该压缩文件拷贝到项目模板目录,使用起来不是很方便。我们希望能像安装文件那样,双击即可完成安装,不需要手工拷贝文件。此时我们还需要一个文件,.vscontent,来制作.vsi安装文件。我们在刚刚导出的项目模板目录中新建名为:SampleProjectTemplate.vscontent的xml文件。它的内容如下:
以上的属性配置就不多介绍了,需要说明的是ProjectSubType属性是指定将该模板安装在Web的项目列表当中。将项目模板的压缩文件和SampleProjectTemplate.vscontent压缩成一个ZIP文件,并且修改后缀名为:.vsi 。然后你可以看到的它的图标发生面什么变化了,双击该文件,我们会得到这样的一个安装向导界面: