65.9K
CodeProject 正在变化。 阅读更多。
Home

创建具有嵌套项的项模板

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.71/5 (6投票s)

2008年1月25日

CPOL

4分钟阅读

viewsIcon

57548

downloadIcon

418

一种模板创建方法,实现 IWizard 以在解决方案中添加嵌套项

引言

许多人一直想知道 Visual Studio 是如何在解决方案资源管理器中实现项的嵌套的。如果我没有解释清楚,我指的是表示一个 Form 对象的三个项的嵌套外观。

  • Form1.cs
  • Form.Designer.cs
  • Form.resx

我博客上的帖子可以在这里找到:这里

背景

Visual Studio 2005 没有选择多个项并创建嵌套方案的功能。要做到这一点,你必须用你喜欢的编辑器打开 *.csproj 文件,然后做一些如这里所述的操作。

我本以为 Visual Studio 2008 会实现这个功能,但它并没有。那么你能做什么呢?一种解决方案是使用来自delarou 的博客的宏。这个想法很简单,但我发现这是唯一一篇解释了如何以编程方式告诉 Visual Studio 嵌套项的技巧的文章。没有这篇帖子我做不到。

但是宏不是我的兴趣所在,因为嵌套项通常是插入模板项的结果。所以在这篇文章中,我将重点介绍如何在创建具有多个项的自定义项模板时实现这一点。例如,一个类型的数据集。

一篇关于如何创建自定义项模板的文章可以在这里找到:这里

我将深入探讨这个过程,因为互联网上有很多关于此的文章。我唯一要说的是,自定义项模板文件 (MyTemplate.vstemplate),它描述了模板和要使用的文件,不允许在其每个项上使用 <DependentUpon> 属性。

所以唯一的解决方案是为模板附加自定义逻辑,该逻辑实现 Microsoft.VisualStudio.TemplateWizard.IWizard 接口的类,并告诉 MyTemplate.vstemplate 使用包含该文件的程序集。

这个解决方案的唯一问题是,所有与模板逻辑相关的程序集必须位于全局程序集缓存 (gac) 中,因此相关的项目必须经过签名。

基础设施

经过大量的艰苦测试,我找到了一个解决方案,它只需要一个类来定义将嵌套项与其父项分离的逻辑。这是通过在继承自 Sarafian.Templates.NestedItemTemplateBase.Base (实现了 IWizard 接口)的类中定义模板逻辑来实现的。

这个类私有地存储了哪个项目是父项目,哪个是子项目。当项被添加到项目并发送到模板逻辑进行处理时,模板在 ProjectItemFinishedGenerating 函数中调用 abstract 函数 GetProjectItemType ,并决定它是父项还是嵌套项。

public void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
{
    ProjectItemTypes type = GetProjectItemType(projectItem);
    switch (type)
    {
        case ProjectItemTypes.Parent:
            this.parentProjectItem = projectItem; 
            break;
        case ProjectItemTypes.Child:
            this.childrenProjectItems.Add(projectItem);
            break;
    }
}

然后,当模板完成后,它会像这样将嵌套项添加到其父项中:

public void RunFinished()
{
    foreach (EnvDTE.ProjectItem item in this.childrenProjectItems)
    {
    string filename = item.get_FileNames(0);
    this.parentProjectItem.ProjectItems.AddFromFile(filename);
    }
}

由于项目被要求放在 gac 中,所以定义了这个生成后事件:

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" 
    /if "$(TargetPath)"

创建模板

在上面的 zip 文件中,我实现了一个示例。

我想创建的模板项包含四个文件:

  • Example.cs
  • Example.Input.cs
  • Example.Output.cs
  • Example.Error.cs

Example.cs 将是父项,其他所有都是它的子项。所以操作方法如下:

protected override ProjectItemTypes GetProjectItemType(EnvDTE.ProjectItem item)
{
    if (item.Name.IndexOf(".Input.cs") > 0)
    {
        return ProjectItemTypes.Child;
    }
    if (item.Name.IndexOf(".Output.cs") > 0)
    {
        return ProjectItemTypes.Child;
    }
    if (item.Name.IndexOf(".Error.cs") > 0)
    {
        return ProjectItemTypes.Child;
    }

    return ProjectItemTypes.Parent;

} 

它们是完全不同的类还是部分类并不重要。出于实际原因,模板 zip 文件所需的整个文件集合都放在一个名为 ExampleItem 的目录中,并且在每个文件的属性中被标记为 Compile none 和 Copy if Newer。

"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" 
    /if "$(TargetPath)"
"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" 
    /l "$(TargetName)"

第一次编译后,会生成一个公钥标记,该标记只需在 MyTemplate.vstemplate 文件中设置一次,如下所示:

<WizardExtension>
    <Assembly>Sarafian.Templates.ExampleItemTemplate, Version=1.0.0.0, 
        Culture=neutral, PublicKeyToken=6df23c9c2e2f12af</Assembly>
    <FullClassName>Sarafian.Templates.ExampleItemTemplate.Template</FullClassName>
</WizardExtension>

之后,模板就可以使用了。

关注点

需要注意的一些事项。因为 Visual Studio 使用全局程序集缓存 (gac) 的缓存版本,对模板二进制方面的任何更改都需要重新启动使用该模板的 Visual Studio。

无法使用传统技术进行调试,但使用了旧技术来理解 IWizard 实现类内部发生的情况。这是我的理解。

首先调用 ShouldAddProjectItem 。如果返回 false,则文件永远不会被模板化。这意味着其中的任何参数都不会被替换。此时 filepath 具有原始文件的值。

在自定义参数替换后,会调用 ProjectItemFinishedGenerating 。此时,项的名称是生成后的名称。例如,如果我想让我的模板命名为 Sarafian,那么对于模板中的 Example.Input.cs,项的名称就是 Sarafian.Input.cs

在所有文件都经过上述函数处理后,会调用 BeforeOpeningFile ,它告诉 Visual Studio 打开文件进行编辑。在这种情况下,项始终对应于 Example.cs

最后调用 RunFinished

当模板是项模板而不是项目模板时,会遵循上述顺序。

有关更多信息,可以在 Ron Petrusha 的这篇文章中找到,这篇文章对我帮助很大。

历史

  • 2008 年 1 月 25 日:首次发布
  • 2008 年 5 月 5 日:添加了 Visual Studio 2008 的解决方案
    未来的实现将有一个命令行,可以动态生成模板的 zip 文件。有趣的是,我找不到使用 Windows zip 功能的命令。任何评论或指导都将不胜感激。
© . All rights reserved.