使用项目模板和向导扩展






4.38/5 (7投票s)
创建自定义项目模板,让您的团队可以用于快速开发。
引言
项目模板和项模板是 Visual Studio 中隐藏的开发宝藏。它们不仅可以缩短开发时间,还能强制实施任何架构概念,尤其是在大型项目中。
这不是我第一次尝试撰写关于项目模板的文章,但我正在尝试解决我在最近工作中开发的所有内容。未来,我可能会扩展这篇文章以涵盖未来的方面。
背景
项目模板和项模板为企业解决方案提供了通用的项目结构。当您在一个或多个解决方案中拥有多个相似的项目时,项目模板非常有用。它提供了通用的项目引用、类结构、默认实现等等。
在此处获取有关项目和项模板的更多信息:此处。
Using the Code
在本文中,我们将创建一个带有向导扩展的自定义项目模板。您可以在 MSDN 网站上找到更多关于项目模板的信息:此处。我们将使用向导扩展在创建项目时获取用户的输入。
在我们的解决方案中,我们将假定所有项目都实现了 MyProjectTemplate.BaseProject
类和 IDisposable
接口。
using MyProjectTemplate.BaseProject;
namespace MyExternalService
{
public class ExternalService : BaseService, IDisposable
{
}
}
创建基础项目模板
要创建项目模板,我们需要一个基础项目模板,其中我们引用外部引用、自定义类、自定义外部文件(将在向导中输入文件)。可以将其扩展到各种组合,如配置文件、命名空间等。
我们可以使用项目模板属性来修改此项目及其类。
创建项目
创建一个项目并添加所有必需的引用,包括 MyProjectTemplate.DLL
。
您还需要将 Request.XML 和 Response.XML 添加到资源文件夹中。
添加默认类
现在向项目中添加一个类 MyExternalService
,并为其实现 BaseService
和 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 "https:///MyExternalService"; }
// do the default request header mapping using requestHeader
}
public void Dispose()
{
//Release local XML file
}
}
}
确保项目能够编译,以便当您创建模板时,您创建的项目不会引入任何错误。
替换项目中的参数
现在我们的项目已准备好替换参数。
在上面的类中,将命名空间和类名 MyExternalService
替换为 $projectname$
。您也可以在 RequestXML
和 ResponseXML
属性中替换 $projectname$
,以便新属性引用新创建项目中的 XML 文件。
在替换完上述类中的参数后,我们的项目将如下所示:
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 "https:///MyExternalService"; }
// do the default request header mapping using requestHeader
}
public void Dispose()
{
//Release local XML file
}
}
}
注意:现在不用担心,您将无法编译项目,但目前是可以的。
导出项目模板
现在选择此项目,然后在 Visual Studio 的“文件”菜单中单击“导出模板”,此时应该会出现“导出模板向导”。
在“导出模板向导”中选择项目,然后单击“下一步”。
提供模板名称、描述、图标和默认预览图像,然后单击“完成”按钮。
将项目导出为项目模板后,您导出的项目现在应该出现在 Visual Studio 的“添加/创建项目”列表中的“项目模板”列表中。
此项目模板仍然是基础的,并且不接受来自用户的输入,例如 XML 文件、URL、超时等。
要为项目模板实现更多自定义,您需要使用向导扩展,在该扩展中,您将创建一个窗体来输入用户的输入,最终将其用于使用项目模板创建新项目。
您可以在 Visual Studio 2005 项目和项模板 中找到详细实现。
创建向导扩展
如前一节所述,要在创建新项目时添加用户输入,我们将添加一个向导扩展,它使用输入窗体。
要创建向导扩展,我们必须添加一个类库项目。在此练习中,我们将执行以下强制性功能。
- 添加一个继承自
IWizard
接口的类,该接口是Microsoft.VisualStudio.TemplateWizard
命名空间的一部分。 - 添加一个 Windows 窗体,并提供诸如“打开文件对话框”、“文本框”和“提交”按钮之类的输入字段。
- 创建一个强类型程序集(使用强密钥),并将其注册到 GAC。
添加一个属性类来存储用户的输入。
添加一个仅具有 get
/set
属性(DTO)的 ProjectProperty
类。此类将有助于存储和传输用户输入数据从窗体。您可以根据自己的需求自定义此类。
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";
}
添加一个包含所有自定义输入的 Windows 窗体。
添加一个包含所有自定义控件的 Windows 窗体,并公开一个类型为 ProjectProperty
的属性。这样,当用户提交窗体时,向导就可以从公开的属性中获取控件值。
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();
}
实现 IWizard 接口。
添加一个实现 IWizard
接口的类。
namespace MyProjectTemplate
{
public class MyCustomWizard : IWizard
{
}
}
IWizard
实现 RunStarted
方法,从此方法显示您的 Windows 窗体,并将所有自定义值(ProjectProperty
)映射到 replacementsDictionary
集合。
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());
}
}
将文件添加到项目中
您无法在 RunStarted
方法中将文件添加到项目结构中,因为在 RunStarted
方法执行时,项目结构已在Temp 文件夹中创建,并且 DTE 正在尝试从Temp 文件夹中读取不存在的文件。
另一个问题是您无法在项目目录中创建任何文件和文件夹,因为在创建文件/文件夹时,IO 会创建项目文件夹,并在 RunStarted
方法之后,DTE 会出现错误,因为它已经拥有项目文件夹,并且它假设项目已经创建。
因此,最终的解决方案是,您需要在项目创建完成后复制文件,这可以通过实现 RunFinished
方法来实现,该方法在 DTE 完成项目创建任务时执行。
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);
}
注册到 GAC
使用强密钥编译上述 DLL 后,将其注册到 GAC。这是一个强制性步骤,因为项目模板可以访问 GAC。
再次更新项目模板
现在我们已经准备好了使用 IWizard
接口的自定义向导扩展。在本节中,我们将向项目中添加替换参数。
在项目模板类中更新参数后,它将如下所示:
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
}
}
}
导出项目
现在,以与之前导出相同的方式再次导出上述项目。
更新 .vstemplate 文件
现在更新 .vstemplate 文件,并使用向导扩展进行更新。
在记事本中打开 .vstemplate 文件,并添加以下代码,然后保存。
<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>
将其压缩并保存在“我的导出模板”位置。
现在,从“我的导出模板”位置压缩整个项目模板和 .csprj 文件。
最后
我们的自定义项目模板(带有向导扩展)几乎准备就绪。如果一切就绪,您将在 Visual Studio 的“添加/创建项目”菜单中看到项目模板。
当您创建一个新项目时,系统将提示用户输入文件和其他输入,这些信息将用于生成类和项目。您可能需要手动将Resources 文件夹包含到项目文件中(我正在处理此部分,以使其自动创建,完成后将通知您)。
通过这种方式,您可以自定义任意数量的组合,并且还可以在Config 文件、XML 文件等中使用。
代码
即将推出……我将在几天内更新代码。