扩展 Visual Studio 第三部分 - 项模板
了解如何通过创建自定义项模板来扩展 Visual Studio 2010。
扩展 Visual Studio
本文是“扩展Visual Studio”系列文章的一部分。
第1部分 - 创建代码片段
第2部分 - 创建插件
第三部分 - 项模板
介绍
在我关于扩展 Visual Studio 系列的第三部分中,我们将介绍项模板。项模板是您选择“添加新项”菜单选项时看到的。
在这里我们可以看到一堆项模板。有些是 Visual Studio 自带的,有些是我自己的。添加您自己的自定义项模板非常简单,但相关的文档却有点少。在本文中,我们将创建一个全新的“ViewModel”项模板 - 一个实现 INotifyPropertyChanged 的类。
别急!
本文介绍的是如何创建项模板 - 请不要担心我们创建的模板功能不是非常强大 - 它仅用于说明目的!请尽量不要过多纠结于 ViewModel,它只是为了让我们有一个比“Hello World”更好的任务!对于那些感兴趣的人,我确实为 MVVM 系统创建了一些功能更强大的项模板,稍后将详细介绍。
开始之前
您需要 Microsoft Visual Studio SDK。我使用的是 Microsoft Visual Studio 2010 SP1,请注意 2010 和 2010 SP1 有不同的安装程序。请从下面的链接下载 SDK。
Microsoft Visual Studio 2010 SDK - http://www.microsoft.com/download/en/details.aspx?id=2680
Microsoft Visual Studio 2010 SP1 SDK - http://www.microsoft.com/download/en/details.aspx?id=21835
安装 SDK 后,您将拥有大量新的项模板和项目模板供您使用 - 它们位于“Visual C# > Extensibility”下:
要开始,请打开 Visual Studio,选择“新建项目”并选择“C# 项模板”。将项目命名为“ViewModelItemTemplate”。
项模板的结构
这是项目外观的截图。它非常简单。
ViewModelItemTemplate.ico - 项模板的图标。
ViewModelItemTemplate.vstemplate - 模板元数据。
Class.cs - 模板将用作基础的文件。
首先,我们将“Class.cs”重命名为“ViewModel.cs”。现在我们将打开 vstemplate 文件并更新元数据。
<?xml version="1.0" encoding="utf-8"?>
<VSTemplate Version="3.0.0" Type="Item" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
<TemplateData>
<Name>View Model</Name>
<Description>Creates a class suitable for use as a View Model, which implements INotifyPropertyChanged.</Description>
<Icon>ViewModelItemTemplate.ico</Icon>
<TemplateID>341358cd-cacb-4f5f-9e5d-be7b3f8a4a97</TemplateID>
<ProjectType>CSharp</ProjectType>
<RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
<NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
<DefaultName>ViewModel.cs</DefaultName>
</TemplateData>
<TemplateContent>
<References>
<Reference>
<Assembly>System</Assembly>
</Reference>
</References>
<ProjectItem ReplaceParameters="true">ViewModel.cs</ProjectItem>
</TemplateContent>
</VSTemplate>
逐一来看,这是我们通过元数据(TemplateData 项)定义的:
- 显示给用户的名称是“View Model”。
- 用户说明,当他们选择模板时会在屏幕右侧显示。
- 使用的图标。
- 模板的 GUID。
- 项目类型(CSharp、VisualBasic 或 Web)。
- 所需的框架版本是 .NET Framework 2.0。
- 将显示此项的父项数量。因此,如果项位于
Category 1 > Category 2 > Category 3 > Category 4 > Item
并且此值为 2,那么当您单击 Category 2、Category 3 和 Category 4 的子项时,它将显示出来。 - 默认名称 - 这是在文件名框中显示的名称。
构建模板本身
构建模板本身非常简单 - 您只需使用一些用美元符号括起来的预定义标记。ViewModel.cs 模板的外观如下:
using System;
using System.Collections.Generic;
$if$ ($targetframeworkversion$ >= 3.5)using System.Linq;
$endif$using System.Text;
using System.ComponentModel;
namespace $rootnamespace$
{
/// <summary>
/// The $safeitemrootname$ class.
/// </summary>
public class $safeitemrootname$ : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies the property changed.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
private void NotifyPropertyChanged(string propertyName)
{
// Get and fire the event.
var theEvent = PropertyChanged;
if (theEvent != null)
theEvent(this, new PropertyChangedEventArgs(propertyName));
}
}
}
天哪,到处都是奇怪的标记,看起来有点丑,但您可以看到发生了什么 - $rootnamespace$ 就是(您猜对了)目标项目的根命名空间,$safeitemroot$ 是他们选择的文件名,其中移除了空格等内容。
就是这样!我们已经准备好了项模板。接下来是创建一个包含该模板的 VSIX 包。我们也可以将其用作测试和调试它的方法。
创建 VSIX 包
在解决方案中,添加一个名为“ViewModelTemplateVsix”的新项目,类型为 VSIX 项目。
双击“source.extension.vsixmanifest”文件。您可以在此处编辑包本身的数据。我们将设置一个更好的标题和一些说明,如下所示。
现在我们可以按“添加内容”来包含我们的项模板。
完成此操作后,请确保 VSIX 包是启动项目,然后按 F5。这将启动 Visual Studio 的实验实例,我们可以在其中测试和调试扩展。创建一个新的 C# 类库项目,然后选择“添加新项...” - 我们的新 ViewModel 项模板已列出 - 选择“Example”作为文件名并添加该项。
这非常棒 - 我们得到了一个具有正确名称并实现 INotifyPropertyChanged 的新类!这是新生成的类。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; namespace ClassLibrary2 { /// <summary> /// The Example class. /// </summary> public class Example : INotifyPropertyChanged { /// <summary> /// Occurs when a property value changes. /// </summary> public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// Notifies the property changed. /// </summary> /// <param name="propertyName">Name of the property.</param> private void NotifyPropertyChanged(string propertyName) { // Get and fire the event. var theEvent = PropertyChanged; if (theEvent != null) theEvent(this, new PropertyChangedEventArgs(propertyName)); } } }
恭喜!到目前为止,您已经学会了如何构建一个基本的项模板。如果这就能满足您的需求,那么您就可以开始了。在本文的下一部分,我们将讨论如何包含项模板的向导,该向导允许我们配置有关如何创建它的选项。
为模板创建向导
在许多情况下,如果您正在创建项模板,那么它会用于更具体和专业化的事物。您可能不仅想创建一个具有特定布局的新项,还想允许用户指定一些选项。在这种情况下,您需要为项模板包含一个向导。或许“向导”这个名字有点误导,实际上我们所要做的只是显示一个带有某些选项的单个对话框。
我现在使用 WPF 来创建我的向导,但为了简单起见,我们将使用 WinForms。向项目添加一个新的 Windows Forms 控件库,并将其命名为“ViewModelWizard”。
向库中添加一个新窗体,并将其命名为 NewViewModelForm。我们将允许用户选择性地包含一个具有指定类型和名称的通知属性。将窗体布局如下。
我将不详细介绍窗体的工作原理,它非常简单 - 它只是将“Include Example”作为一个布尔属性公开,并将 Example Name 和 Example Type 作为字符串公开。现在我们需要实际创建向导类。向项目中添加一个名为“ViewModelWizard”的新类。此时,还要添加对以下库的引用:
- ENVDTE
- Microsoft.VisualStudio.TemplateWizardInterface
如下所示:
Wizard 类必须实现“IWizard”。右键单击“IWizard”行并选择“实现接口”。您的类现在应该如下所示:
public class ViewModelWizard : IWizard
{
public void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
{
throw new NotImplementedException();
}
public void ProjectFinishedGenerating(EnvDTE.Project project)
{
throw new NotImplementedException();
}
public void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
{
throw new NotImplementedException();
}
public void RunFinished()
{
throw new NotImplementedException();
}
public void RunStarted(object automationObject,
Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
throw new NotImplementedException();
}
public bool ShouldAddProjectItem(string filePath)
{
throw new NotImplementedException();
}
}
我们唯一关心的函数是“RunStarted” - 在此函数中,我们将显示窗体并将指定的值添加到“replacementsDictionary”。这将允许我们在模板中使用这些标记。更新类以如下所示:
public class ViewModelWizard : IWizard
{
public void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
{
}
public void ProjectFinishedGenerating(EnvDTE.Project project)
{
}
public void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
{
}
public void RunFinished()
{
}
public void RunStarted(object automationObject,
Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
// Create the form.
var form = new NewViewModelForm();
// Show the form.
form.ShowDialog();
// Add the options to the replacementsDictionary.
replacementsDictionary.Add("$IncludeExample$", form.IncludeExample ? "1" : "0");
replacementsDictionary.Add("$PropertyName$", form.PropertyName);
replacementsDictionary.Add("$PropertyType$", form.PropertyType);
}
public bool ShouldAddProjectItem(string filePath)
{
return true;
}
}
您可以看到,我们所做的只是显示窗体并将选项添加到替换字典中。字典只能包含字符串 - 所以对于布尔值使用“1”或“0”!
现在我们可以更新实际的模板(ViewModel.cs)本身,以使用这些标记。我们将设置一个基于 $IncludeExample$ 的条件块,该块将添加一个名为 $PropertyName$ 类型为 $PropertyType$ 的属性,该属性使用 NotifyPropertyChanged。将 ViewModel.cs 更新如下:
using System;
using System.Collections.Generic;
$if$ ($targetframeworkversion$ >= 3.5)using System.Linq;
$endif$using System.Text;
using System.ComponentModel;
namespace $rootnamespace$
{
/// <summary>
/// The $safeitemrootname$ class.
/// </summary>
public class $safeitemrootname$ : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies the property changed.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
private void NotifyPropertyChanged(string propertyName)
{
// Get and fire the event.
var theEvent = PropertyChanged;
if (theEvent != null)
theEvent(this, new PropertyChangedEventArgs(propertyName));
} $if$ ($IncludeExample$ == 1)
/// <summary>
/// The value of $PropertyName$.
/// </summary>
private $PropertyType$ $PropertyName$Value;
/// <summary>
/// Gets or sets $PropertyName$.
/// </summary>
public $PropertyType$ $PropertyName$
{
get { return $PropertyName$; }
set
{
if( $PropertyName$Value != value)
{
$PropertyName$Value = value;
NotifyPropertyChanged($PropertyName$);
}
}
} $endif$
}
}
在测试之前,我们需要做三件事 - 对向导程序集进行签名,将向导程序集安装到 GAC,然后更新项模板以使用向导。
要对程序集进行签名,请打开向导项目设置,选择“签名”,勾选“签名程序集”并输入一个新密钥 - 不需要密码。
要将程序集安装到 GAC,请将以下内容添加到向导项目的生成后事件中:
32 位机器
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\gacutil" /if "$(TargetPath)"
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\sn" -T "$(TargetPath)"
64 位机器
"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\gacutil" /if "$(TargetPath)"
"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\sn" -T "$(TargetPath)"
这只是构建项目。生成后事件会将向导安装到 GAC 并显示强名称令牌。
Assembly successfully added to the cache Microsoft (R) .NET Framework Strong Name Utility Version 3.5.30729.1 Copyright (c) Microsoft Corporation. All rights reserved. Public key token is e0700af0fcefc5c7
我们需要此令牌来完成项目的最后部分。打开 ViewModelItemTemplate.vsitemtemplate 文件,并在 TemplateContent 的结束标记后添加:
<!-- Use the appropriate IWizard from the ViewModelWizard assembly. -->
<WizardExtension>
<Assembly>
ViewModelWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e0700af0fcefc5c7
</Assembly>
<FullClassName>ViewModelWizard.ViewModelWizard</FullClassName>
</WizardExtension>
别忘了使用正确的公钥令牌(以粗体突出显示)。
现在按 F5。向类库添加一个新的 ViewModel - 添加时我们会看到创建的用户界面,并且可以指定一个示例属性。
结果如下:
using System.ComponentModel;
namespace ClassLibrary2
{
/// <summary>
/// The Example class.
/// </summary>
public class Example : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies the property changed.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
private void NotifyPropertyChanged(string propertyName)
{
// Get and fire the event.
var theEvent = PropertyChanged;
if (theEvent != null)
theEvent(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// The value of FirstName.
/// </summary>
private string FirstNameValue;
/// <summary>
/// Gets or sets FirstName.
/// </summary>
public string FirstName
{
get { return FirstName; }
set
{
if (FirstNameValue != value)
{
FirstNameValue = value;
NotifyPropertyChanged(FirstName);
}
}
}
}
}
最后的思考
希望您喜欢这篇文章 - 请随时评论您的想法、问题或潜在的改进!