创建基本项目系统,第 2 部分

本系列的第一个演练: 创建基本项目系统(第 1 部分)演示如何创建基本项目系统。 本演练通过添加 Visual Studio 模板、属性页和其他功能来构建基本项目系统。 必须先完成第一个演练,然后才能开始本演练。

本演练介绍如何创建具有项目文件扩展名 .myproj 的项目类型。 若要完成本演练,无需创建自己的语言,因为该演练从现有的 Visual C# 项目系统借用。

本演练介绍如何完成以下任务:

  • 创建 Visual Studio 模板。

  • 部署 Visual Studio 模板。

  • “新建项目 ”对话框中创建项目类型子节点。

  • 在 Visual Studio 模板中启用参数替换。

  • 创建项目属性页。

注意

本演练中的步骤基于 C# 项目。 但是,除了文件扩展名和代码等细节外,还可以对 Visual Basic 项目使用相同的步骤。

创建 Visual Studio 模板

通过使用 Visual Studio 模板(.vstemplate 文件)而不是基本项目模板,可以控制模板在 “新建项目 ”对话框中的显示方式以及模板参数的替换方式。 .vstemplate 文件是一个 XML 文件,描述使用项目系统模板创建项目时如何包含源文件。 项目系统本身是通过收集 .vstemplate 文件和 .zip 文件中的源文件生成的,并通过将 .zip 文件复制到 Visual Studio 已知的位置进行部署。 本演练稍后将更详细地介绍此过程。

  1. 在 Visual Studio 中,打开通过以下 创建基本项目系统(第 1 部分)创建的 SimpleProject 解决方案。

  2. SimpleProjectPackage.cs 文件中,找到 ProvideProjectFactory 属性。 将第二个参数(项目名称)替换为 null,并将第四个参数(项目模板文件夹的路径)替换为“.\\NullPath”,如下所示。

    [ProvideProjectFactory(typeof(SimpleProjectFactory), null,
        "Simple Project Files (*.myproj);*.myproj", "myproj", "myproj",
        ".\\NullPath",
    LanguageVsTemplate = "SimpleProject")]
    
  3. 将名为 SimpleProject.vstemplateXML 文件添加到 \Templates\Projects\SimpleProject\ 文件夹。

  4. 将 SimpleProject.vstemplate 的内容替换为以下代码。

    <VSTemplate Version="2.0.0" Type="Project"
        xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
      <TemplateData>
        <Name>SimpleProject Application</Name>
        <Description>
          A project for creating a SimpleProject application
        </Description>
        <Icon>SimpleProject.ico</Icon>
        <ProjectType>SimpleProject</ProjectType>
      </TemplateData>
      <TemplateContent>
        <Project File="SimpleProject.myproj" ReplaceParameters="true">
          <ProjectItem ReplaceParameters="true" OpenInEditor="true">
            Program.cs
          </ProjectItem>
          <ProjectItem ReplaceParameters="true" OpenInEditor="false">
            AssemblyInfo.cs
          </ProjectItem>
        </Project>
      </TemplateContent>
    </VSTemplate>
    
  5. “属性”窗口中,选择\Templates\Projects\SimpleProject\ 文件夹中的所有五个文件,并将生成操作设置为 ZipProject

    Simple Project Folder

    TemplateData <> 部分确定“新建项目”对话框中 SimpleProject 项目类型的位置和外观,如下所示:

  • Name <> 元素将项目模板命名为 SimpleProject 应用程序。

  • <Description> 元素包含选择项目模板时显示在“新建项目”对话框中的说明。

  • <Icon> 元素指定与 SimpleProject 项目类型一起显示的图标。

  • ProjectType <> 元素在“新建项目”对话框中命名项目类型。 此名称替换 ProvideProjectFactory 属性的项目名称参数。

    注意

    <ProjectType> 元素必须与 SimpleProjectPackage.cs 文件中属性的参数ProvideProjectFactory匹配LanguageVsTemplate

    <TemplateContent> 部分介绍创建新项目时生成的这些文件:

  • SimpleProject.myproj

  • Program.cs

  • AssemblyInfo.cs

    这三个文件都 ReplaceParameters 设置为 true,可启用参数替换。 Program.cs 文件已OpenInEditor设置为 true,这会导致在创建项目时在代码编辑器中打开该文件。

    有关 Visual Studio 模板架构中的元素的详细信息,请参阅 Visual Studio 模板架构参考

注意

如果项目有多个 Visual Studio 模板,则每个模板都位于单独的文件夹中。 该文件夹中的每个文件都必须将 生成操作 设置为 ZipProject

添加最小 .vsct 文件

Visual Studio 必须在安装模式下运行才能识别新的或修改的 Visual Studio 模板。 安装模式要求 存在 .vsct 文件。 因此,必须将最小 .vsct 文件添加到项目。

  1. 将名为 SimpleProject.vsct 的 XML 文件添加到 SimpleProject 项目。

  2. 将 SimpleProject.vsct 文件的内容替换为以下代码。

    <?xml version="1.0" encoding="utf-8" ?>
    <CommandTable
      xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable">
    </CommandTable>
    
  3. 将此 文件的生成操作 设置为 VSCTCompile。 只能在 .csproj 文件中执行此操作,不能在 “属性” 窗口中执行此操作。 确保 此时此文件的生成操作 设置为 “无 ”。

    1. 右键单击 SimpleProject 节点,然后单击“ 编辑 SimpleProject.csproj”。

    2. .csproj 文件中,找到 SimpleProject.vsct 项。

      <None Include="SimpleProject.vsct" />
      
    3. 将生成操作更改为 VSCTCompile

      <VSCTCompile Include="SimpleProject.vsct" />
      
    4. 项目文件并关闭编辑器。

    5. 保存 SimpleProject 节点,然后在解决方案资源管理器单击“重新加载项目”。

检查 Visual Studio 模板生成步骤

当更改 .vstemplate 文件或重新生成包含 .vstemplate 文件的项目时,VSPackage 项目生成系统通常会在安装模式下运行 Visual Studio。 接下来,可以将 MSBuild 的详细级别设置为 Normal 或更高版本。

  1. “工具” 菜单上,单击 “选项”

  2. 展开“项目和解决方案”节点,然后选择“生成并运行”。

  3. MSBuild 项目生成输出详细程度 设置为 Normal。 单击“确定”。

  4. 重新生成 SimpleProject 项目。

    创建 .zip 项目文件的生成步骤应类似于以下示例。

ZipProjects:
1>  Zipping ProjectTemplates
1>  Zipping <path>\SimpleProject\SimpleProject\obj\Debug\SimpleProject.zip...
1>  Copying file from "<path>\SimpleProject\SimpleProject\obj\Debug\SimpleProject.zip" to "<%LOCALAPPDATA%>\Microsoft\VisualStudio\14.0Exp\ProjectTemplates\\\\SimpleProject.zip".
1>  Copying file from "<path>\SimpleProject\SimpleProject\obj\Debug\SimpleProject.zip" to "bin\Debug\\ProjectTemplates\\\\SimpleProject.zip".
1>  SimpleProject -> <path>\SimpleProject\SimpleProject\bin\Debug\ProjectTemplates\SimpleProject.zip
1>ZipItems:
1>  Zipping ItemTemplates
1>  SimpleProject ->

部署 Visual Studio 模板

Visual Studio 模板不包含路径信息。 因此,模板 .zip 文件必须部署到 Visual Studio 已知的位置。 ProjectTemplates 文件夹的位置通常是 <%LOCALAPPDATA%>\Microsoft\VisualStudio\14.0Exp\ProjectTemplates

若要部署项目工厂,安装程序必须具有管理员权限。 它在 Visual Studio 安装节点下部署模板: ...\Microsoft Visual Studio 14.0\Common7\IDE\ProjectTemplates

测试 Visual Studio 模板

测试项目工厂,以查看它是否使用 Visual Studio 模板创建项目层次结构。

  1. 重置 Visual Studio SDK 实验实例。

    在 Windows 7 上:在 “开始 ”菜单上,找到 Microsoft Visual Studio/Microsoft Visual Studio SDK/Tools 文件夹,然后选择“ 重置 Microsoft Visual Studio 实验实例”。

    在更高版本的 Windows 上:在 “开始” 屏幕上,键入 “重置 Microsoft Visual Studio <版本> 实验实例”。

  2. 此时会显示命令提示符窗口。 看到“按任意键继续”时,单击 Enter。 窗口关闭后,打开 Visual Studio。

  3. 重新生成 SimpleProject 项目并开始调试。 这将显示实验实例。

  4. 在实验实例中,创建 SimpleProject 项目。 在 “新建项目 ”对话框中,选择 “SimpleProject”。

  5. 应会看到 SimpleProject 的新实例。

    Simple Project New Instance

    My Project New Instance

创建项目类型子节点

可以在“新建项目”对话框中将子节点添加到项目类型节点。 例如,对于 SimpleProject 项目类型,可以为控制台应用程序、窗口应用程序、Web 应用程序等提供子节点。

子节点通过更改项目文件并将 OutputSubPath> 子节点添加到 <<ZipProject> 元素来创建。 在生成或部署期间复制模板时,每个子节点将成为项目模板文件夹的子文件夹。

本部分介绍如何为 SimpleProject 项目类型创建控制台子节点。

  1. \Templates\Projects\SimpleProject\ 文件夹重命名为 \Templates\Projects\ConsoleApp\

  2. “属性”窗口中,选择\Templates\Projects\ConsoleApp\ 文件夹中的所有五个文件,并确保生成操作设置为 ZipProject

  3. 在 SimpleProject.vstemplate 文件中,在 TemplateData> 节末尾<添加以下行,紧跟在结束标记之前。

    <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
    

    这会导致控制台应用程序模板同时显示在控制台子节点和 SimpleProject 父节点中,该节点高于子节点一级。

  4. 保存 SimpleProject.vstemplate 文件。

  5. .csproj 文件中,将 OutputSubPath> 添加到<每个 ZipProject 元素。 像以前一样卸载项目,然后编辑项目文件。

  6. <找到 ZipProject> 元素。 对于每个 <ZipProject> 元素,请添加 OutputSubPath><元素并为其提供值控制台。 The ZipProject

    <ZipProject Include="Templates\Projects\ConsoleApp\AssemblyInfo.cs">
      <OutputSubPath>Console</OutputSubPath>
    </ZipProject>
    <ZipProject Include="Templates\Projects\ConsoleApp\Program.cs">
      <OutputSubPath>Console</OutputSubPath>
    </ZipProject>
    <ZipProject Include="Templates\Projects\ConsoleApp\SimpleProject.myproj">
      <OutputSubPath>Console</OutputSubPath>
    </ZipProject>
    <ZipProject Include="Templates\Projects\ConsoleApp\SimpleProject.vstemplate">
      <OutputSubPath>Console</OutputSubPath>
    </ZipProject>
    <ZipProject Include="Templates\Projects\ConsoleApp\SimpleProject.ico">
      <OutputSubPath>Console</OutputSubPath>
    </ZipProject>
    
  7. 将此 <PropertyGroup> 添加到项目文件:

    <PropertyGroup>
      <VsTemplateLanguage>SimpleProject</VsTemplateLanguage>
    </PropertyGroup>
    
  8. 保存项目文件并重新加载项目。

测试项目类型子节点

测试修改后的项目文件,以查看控制台子节点是否显示在“新建项目”对话框中。

  1. 运行“重置 Microsoft Visual Studio 实验实例”工具。

  2. 重新生成 SimpleProject 项目并开始调试。 实验实例应显示

  3. “新建项目 ”对话框中,单击 SimpleProject 节点。 控制台 应用程序 模板应显示在 “模板 ”窗格中。

  4. 展开 SimpleProject 节点。 此时应显示控制台子节点。 SimpleProject 应用程序模板继续显示在“模板”窗格中。

  5. 单击“取消并停止调试。

    Simple Project Rollup

    Simple Project Console Node

替换项目模板参数

  • 创建基本项目系统,第 1 部分演示了如何覆盖 ProjectNode.AddFileFromTemplate 方法以执行基本类型的模板参数替换。 本部分介绍如何使用更复杂的 Visual Studio 模板参数。

在“新建项目”对话框中使用 Visual Studio 模板创建项目时,模板参数将替换为用于自定义项目的字符串。 模板参数是一个以美元符号开头和结尾的特殊令牌,例如,$time$。 以下两个参数特别适用于在基于模板的项目中启用自定义:

  • $GUID[1-10]$ 替换为新的 Guid。 最多可以指定 10 个唯一 GUID,例如 $guid 1$。

  • $safeprojectname$ 是“新建项目”对话框中用户提供的名称,经过修改以删除所有不安全字符和空格。

    有关模板参数的完整列表,请参阅模板参数

替换项目模板参数

  1. SimpleProjectNode.cs 文件中,删除 AddFileFromTemplate 该方法。

  2. \Templates\Projects\ConsoleApp\SimpleProject.myproj 文件中,找到 <RootNamespace> 属性并将其值更改为 $safeprojectname$。

    <RootNamespace>$safeprojectname$</RootNamespace>
    
  3. \Templates\Projects\SimpleProject\Program.cs 文件中,将文件的内容替换为以下代码:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;    // Guid
    
    namespace $safeprojectname$
    {
        [Guid("$guid1$")]
        public class $safeprojectname$
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Hello VSX!!!");
                Console.ReadKey();
            }
        }
    }
    
  4. 重新生成 SimpleProject 项目并开始调试。 应显示实验实例。

  5. 创建新的 SimpleProject 控制台应用程序。 (在“项目类型”窗格,选择“SimpleProject”。在 Visual Studio 安装的模板,选择“控制台应用程序”。

  6. 在新建的项目中,打开 Program.cs。 它应如下所示(文件中的 GUID 值将有所不同)。

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;    // Guid
    
    namespace Console_Application1
    {
        [Guid("00000000-0000-0000-00000000-00000000)"]
        public class Console_Application1
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Hello VSX!!!");
                Console.ReadKey();
            }
        }
    }
    

创建项目属性页

可以为项目类型创建属性页,以便用户可以查看和更改基于模板的项目中的属性。 本部分介绍如何创建独立于配置的属性页。 此基本属性页使用属性网格显示你在属性页类中公开的公共属性。

SettingsPage 基类派生属性页类。 类提供 SettingsPage 的属性网格知道大多数基元数据类型,并知道如何显示它们。 此外, SettingsPage 该类还知道如何将属性值保存到项目文件。

在本部分中创建的属性页可用于更改和保存这些项目属性:

  • AssemblyName

  • OutputType

  • RootNamespace。

  1. SimpleProjectPackage.cs 文件中,将此属性 ProvideObject 添加到 SimpleProjectPackage 类:

    [ProvideObject(typeof(GeneralPropertyPage))]
    public sealed class SimpleProjectPackage : ProjectPackage
    

    这会向 COM 注册属性页类 GeneralPropertyPage

  2. SimpleProjectNode.cs 文件中,将这两个重写的方法添加到 SimpleProjectNode 类:

    protected override Guid[] GetConfigurationIndependentPropertyPages()
    {
        Guid[] result = new Guid[1];
        result[0] = typeof(GeneralPropertyPage).GUID;
        return result;
    }
    protected override Guid[] GetPriorityProjectDesignerPages()
    {
        Guid[] result = new Guid[1];
        result[0] = typeof(GeneralPropertyPage).GUID;
        return result;
    }
    

    这两种方法都返回属性页 GUID 数组。 GeneralPropertyPage GUID 是数组中唯一的元素,因此“ 属性页 ”对话框仅显示一页。

  3. 将名为 GeneralPropertyPage.cs 的类文件添加到 SimpleProject 项目。

  4. 使用以下代码替换此文件的内容:

    using System;
    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Project;
    using System.ComponentModel;
    
    namespace SimpleProject
    {
        [ComVisible(true)]
        [Guid("6BC7046B-B110-40d8-9F23-34263D8D2936")]
        public class GeneralPropertyPage : SettingsPage
        {
            private string assemblyName;
            private OutputType outputType;
            private string defaultNamespace;
    
            public GeneralPropertyPage()
            {
                this.Name = "General";
            }
    
            [Category("AssemblyName")]
            [DisplayName("AssemblyName")]
            [Description("The output file holding assembly metadata.")]
            public string AssemblyName
            {
                get { return this.assemblyName; }
            }
            [Category("Application")]
            [DisplayName("OutputType")]
            [Description("The type of application to build.")]
            public OutputType OutputType
            {
                get { return this.outputType; }
                set { this.outputType = value; this.IsDirty = true; }
            }
            [Category("Application")]
            [DisplayName("DefaultNamespace")]
            [Description("Specifies the default namespace for added items.")]
            public string DefaultNamespace
            {
                get { return this.defaultNamespace; }
                set { this.defaultNamespace = value; this.IsDirty = true; }
            }
    
            protected override void BindProperties()
            {
                this.assemblyName = this.ProjectMgr.GetProjectProperty("AssemblyName", true);
                this.defaultNamespace = this.ProjectMgr.GetProjectProperty("RootNamespace", false);
    
                string outputType = this.ProjectMgr.GetProjectProperty("OutputType", false);
                this.outputType = (OutputType)Enum.Parse(typeof(OutputType), outputType);
            }
    
            protected override int ApplyChanges()
            {
                this.ProjectMgr.SetProjectProperty("AssemblyName", this.assemblyName);
                this.ProjectMgr.SetProjectProperty("OutputType", this.outputType.ToString());
                this.ProjectMgr.SetProjectProperty("RootNamespace", this.defaultNamespace);
                this.IsDirty = false;
    
                return VSConstants.S_OK;
            }
        }
    }
    

    GeneralPropertyPage 类公开三个公共属性 AssemblyName、OutputType 和 RootNamespace。 由于 AssemblyName 没有 set 方法,因此它显示为只读属性。 OutputType 是一个枚举常量,因此它显示为下拉列表。

    SettingsPage 类提供 ProjectMgr 用于保存属性。 该方法 BindProperties 用于 ProjectMgr 检索持久化属性值并设置相应的属性。 该方法 ApplyChanges 用于 ProjectMgr 获取属性的值并将其保存到项目文件中。 属性集方法设置为 IsDirty true 以指示属性必须持久化。 保存项目或解决方案时发生持久性。

  5. 重新生成 SimpleProject 解决方案并开始调试。 应显示实验实例。

  6. 在实验实例中,创建新的 SimpleProject 应用程序。

  7. Visual Studio 调用项目工厂,以使用 Visual Studio 模板创建项目。 新的 Program.cs 文件在代码编辑器中打开。

  8. 右键单击解决方案资源管理器中的项目节点,然后单击“属性”。 属性页 ”对话框。

    Simple Project Property Page

测试项目属性页

现在可以测试是否可以修改和更改属性值。

  1. “MyConsoleApplication 属性页 ”对话框中,将 DefaultNamespace 更改为 MyApplication

  2. 选择 OutputType 属性,然后选择 “类库”。

  3. 单击“应用”,然后单击“确定”

  4. 重新打开“ 属性页 ”对话框,并验证更改是否已持久保存。

  5. 关闭 Visual Studio 的实验实例。

  6. 重新打开实验实例。

  7. 重新打开“ 属性页 ”对话框,并验证更改是否已持久保存。

  8. 关闭 Visual Studio 的实验实例。 Close the experimental instance