使用 Visual Studio 扩展的基于规则的 UI 上下文

当激活某些已知 UIContext 时,Visual Studio 允许加载 VSPackage。 但是,这些 UI 上下文并未细化,这样扩展作者没有什么选择,只能选择在真正希望加载 VSPackage 那一刻之前激活的可用 UI 上下文。 有关熟知的 UI 上下文的列表,请参阅 KnownUIContexts

加载包可能会产生性能影响,提前加载包并不是最佳做法。 Visual Studio 2015 引入了基于规则的 UI 上下文的概念,它是一种机制,允许扩展作者定义激活 UI 上下文并加载关联 VSPackage 的精确条件。

基于规则的 UI 上下文

“规则”由一个新的 UI 上下文 (GUID) 和一个布尔表达式组成,表达式引用一个或多个“条件”,通过逻辑“and”、“or”、“not”运算进行组合。 “条件”在运行时动态计算,每当表达式的任何条件发生更改时,都会重新计算表达式。 当表达式的计算结果为 true 时,将激活关联的 UI 上下文。 否则,将取消激活 UI 上下文。

可以通过多种方式来使用基于规则的 UI 上下文:

  1. 指定命令和工具窗口的可见性约束。 可以在满足 UI 上下文规则之前隐藏命令/工具窗口。

  2. 作为自动加载约束:仅在满足规则时自动加载包。

  3. 作为延迟任务:延迟加载,直到已经过指定的间隔而且仍满足规则。

    任何 Visual Studio 扩展都可以使用该机制。

创建基于规则的 UI 上下文

假设你有一个名为 TestPackage 的扩展,它提供一个菜单命令,该命令仅适用于扩展名为 .config 的文件。 在 VS2015 之前,最佳选择是在激活 SolutionExistsAndFullyLoadedContext UI 上下文后加载 TestPackage。 以这种方式加载 TestPackage 并不有效,因为加载的解决方案甚至可能不包含 .config 文件。 这些步骤演示了基于规则的 UI 上下文如何在仅当选择具有 .config 扩展名的文件时用于激活 UI 上下文,以及如何在激活 UI 上下文时加载 TestPackage。

  1. 定义新的 UIContext GUID 并添加到 VSPackage 类 ProvideAutoLoadAttributeProvideUIContextRuleAttribute

    例如,假设要添加新的 UIContext“UIContextGuid”。 创建的 GUID(可以通过单击工具>创建 GUID 来创建 GUID)为“8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B”。 然后,在包类中添加以下声明:

    public const string UIContextGuid = "8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B";
    

    对于这些属性,请添加以下值:(稍后将解释这些属性的详细信息)

    [ProvideAutoLoad(TestPackage.UIContextGuid)]
    [ProvideUIContextRule(TestPackage.UIContextGuid,
        name: "Test auto load",
        expression: "DotConfig",
        termNames: new[] { "DotConfig" },
        termValues: new[] { "HierSingleSelectionName:.config$" })]
    

    这些元数据定义新的 UIContext GUID (8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B) 和引用单个条件“DotConfig”的表达式。 每当活动层次结构中的当前选定内容具有与正则表达式模式“\.config$”(以 .config 结尾)匹配的名称时,“DotConfig”条件的计算结果为 true。 该值(默认值)为可用于调试的规则定义可选名称。

    属性的值将添加到生成期间生成的 pkgdef 中。

  2. 在 TestPackage 命令的 VSCT 文件中,将“DynamicVisibility”标志添加到相应的命令:

    <CommandFlag>DynamicVisibility</CommandFlag>
    
  3. 在 VSCT 的 VisibilityConstraints 部分中,将相应的命令绑定到 #1 中定义的新 UIContext GUID:

    <VisibilityConstraints>
        <VisibilityItem guid="guidTestPackageCmdSet" id="TestId"  context="UIContextGuid"/>
    </VisibilityConstraints>
    
  4. 在“符号”部分中,添加 UIContext 的定义:

    <GuidSymbol name="UIContextGuid" value="{8B40D5E2-5626-42AE-99EF-3DD1EFF46E7B}" />
    

    现在,仅当解决方案资源管理器中的选定项为 .config 文件时,才会显示 *.config 文件的上下文菜单命令,而且只有在选择其中一个命令后才会加载包。

    接下来,使用调试器确认包仅在期望的时间加载。 调试 TestPackage:

  5. Initialize 方法中设置断点。

  6. 生成 TestPackage 并开始调试。

  7. 创建或打开一个项目。

  8. 选择扩展名为 .config 以外的任何文件。不应命中断点。

  9. 选择 App.Config 文件。

    TestPackage 在断点处加载并停止。

为 UI 上下文添加更多规则

由于 UI 上下文规则是布尔表达式,因此可以为 UI 上下文添加更多受限规则。 例如,在上述 UI 上下文中,可以指定仅在加载具有项目的解决方案时规则才适用。 这样,如果以独立文件的形式打开 .config 文件,而不是将该文件作为项目的一部分打开,命令将不会显示。

[ProvideAutoLoad(TestPackage.UIContextGuid)]
[ProvideUIContextRule(TestPackage.UIContextGuid,
    name: "Test auto load",
    expression: "(SingleProject | MultipleProjects) & DotConfig",
    termNames: new[] { "SingleProject", "MultipleProjects","DotConfig" },
    termValues: new[] { VSConstants.UICONTEXT.SolutionHasSingleProject_string , VSConstants.UICONTEXT.SolutionHasMultipleProjects_string , "HierSingleSelectionName:.config$" })]

现在,表达式引用了三个条件。 前两个条件“SingleProject”和“MultipleProjects”是指其他熟知的 UI 上下文(按其 GUID)。 第三个条件“DotConfig”是本文前面定义的基于规则的 UI 上下文。

延迟激活

规则可以具有可选的“延迟”。 延迟以毫秒为单位指定。 如果存在延迟,则延迟会导致规则的 UI 上下文的激活或停用延后该时间间隔。 如果规则在延迟间隔之前更改回到先前的状态,则不会发生任何操作。 此机制可用于“交错”初始化步骤 - 尤其不依赖计时器或注册空闲通知的一次性初始化。

例如,可以指定测试加载规则延迟 100 毫秒:

[ProvideAutoLoad(TestPackage.UIContextGuid)]
[ProvideUIContextRule(TestPackage.UIContextGuid,
    name: "Test auto load",
    expression: "DotConfig",
    termNames: new[] { "DotConfig" },
    termValues: new[] { "HierSingleSelectionName:.config$" },
    delay: 100)]

条件类型

下面是支持的各种类型的条件:

术语 说明
{nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} GUID 引用 UI 上下文。 只要 UI 上下文处于活动状态,条件将为 true,否则为 false。
HierSingleSelectionName:<pattern> 只要活动层次结构中的选定内容是单个项,而且所选项的名称与“pattern”提供的 .NET 正则表达式匹配,则条件为 true。
UserSettingsStoreQuery:<query> “query”代表用户设置存储区的完整路径,该路径必须计算为非零值。 查询在最后一个斜杠处拆分为“collection”和“propertyName”。
ConfigSettingsStoreQuery:<query> “query”表示配置设置存储区的完整路径,该路径必须计算为非零值。 查询在最后一个斜杠处拆分为“collection”和“propertyName”。
ActiveProjectFlavor:<projectTypeGuid> 每当当前所选项目进行了风格化处理(聚合)并且具有与给定项目类型 GUID 匹配的风格时,条件都将为 true。
ActiveEditorContentType:<contentType> 当所选文档是具有给定内容类型的文本编辑器时,条件为 true。 注意:重命名所选文档时,在文件关闭并重新打开之后,此条件才会刷新。
ActiveProjectCapability:<Expression> 当活动项目功能与提供的表达式匹配时,条件为 true。 表达式可以是 VB |CSharp 之类的内容。
SolutionHasProjectCapability:<Expression> 与上述内容类似,但当解决方案包含与表达式匹配的任何已加载项目时,条件为 true。
SolutionHasProjectFlavor:<projectTypeGuid> 每当解决方案具有已风格化处理(聚合)的项目,而且具有与给定项目类型 GUID 匹配的风格时,条件都将为 true。
ProjectAddedItem:<pattern> 在将与“pattern”匹配的文件添加到打开的解决方案中的项目时,该条件为 true。
ActiveProjectOutputType:<outputType> 当活动项目的输出类型完全匹配时,条件为 true。 outputType 可以是整数或 __VSPROJOUTPUTTYPE 类型。
ActiveProjectBuildProperty:<buildProperty>=<regex> 如果活动项目具有指定的生成属性,而且属性值与提供的正则表达式筛选器匹配,则条件为 true。 有关生成属性的更多详细信息,请参阅 MSBuild 项目文件中的持久保存数据
SolutionHasProjectBuildProperty:<buildProperty>=<regex> 如果解决方案具有带指定生成属性的已加载项目,并且属性值与提供的正则表达式筛选器匹配,则条件为 true。

与跨版本扩展的兼容性

基于规则的 UI 上下文是 Visual Studio 2015 中的一项新功能,不会移植到早期版本。 不移植到早期版本会产生面向多个 Visual Studio 版本的扩展/包的问题。 这些版本必须在 Visual Studio 2013 及更早版本中自动加载,但可以从基于规则的 UI 上下文受益,以防止在 Visual Studio 2015 中自动加载。

为了支持此类包,注册表中的 AutoLoadPackages 条目现在可以在其值字段中提供一个标志,以指示应在 Visual Studio 2015 及更高版本中跳过该条目。 可以通过向 PackageAutoLoadFlags 添加标志选项来完成此操作。 VSPackages 现在可以将 SkipWhenUIContextRulesActive 选项添加到其 ProvideAutoLoadAttribute 属性,以指示应在 Visual Studio 2015 及更高版本中忽略该条目。

可扩展 UI 上下文规则

有些时候,包不能使用静态 UI 上下文规则。 例如,假设你有一个支持扩展性的包,以便命令状态基于导入的 MEF 提供程序支持的编辑器类型。 如果有支持当前编辑类型的扩展,则将启用该命令。 在这种情况下,包本身不能使用静态 UI 上下文规则,因为条件会根据哪些 MEF 扩展可用而发生更改。

为了支持此类包,基于规则的 UI 上下文支持硬编码的表达式“*”,该表达式指示它下面的所有条件都将与 OR 联接。 这使主包能够定义已知的基于规则的 UI 上下文,并将其命令状态绑定到此上下文。 之后,面向主包的任何 MEF 扩展都可以为它支持的编辑器添加其条件,而不会影响其他条件或主表达式。

构造函数 ProvideExtensibleUIContextRuleAttribute 文档显示可扩展 UI 上下文规则的语法。