Visual Studio扩展UI上下文规则失效:右键菜单无法按需显示
我正在开发一款Visual Studio扩展,用于实现JSON文件的常规操作。目标是仅在解决方案资源管理器中右键点击JSON文件时显示对应的上下文菜单项。目前命令可正常运行,但会显示在所有文件上;即使参考微软官网的.config/.cs文件示例配置,问题仍存在。
现有配置如下:
Package类定义
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid(QuickSwapSettingsExtensionPackage.PackageGuidString)] [ProvideMenuResource("Menus.ctmenu", 1)] [ProvideAutoLoad(UIContextGuid, PackageAutoLoadFlags.BackgroundLoad)] [ProvideUIContextRule(UIContextGuid, name: "Apply only to JSON files", expression: "DotConfig", termNames: new[] { "DotConfig" }, termValues: new[] { "HierSingleSelectionName:.config$" })] public sealed class QuickSwapSettingsExtensionPackage : AsyncPackage { public const string PackageGuidString = "d6069cfa-a050-490c-bf8a-5ddb780e204d"; public const string UIContextGuid = "2cfcc18c-f6b5-418a-8531-2b8f3bf53cf5"; ... }
VSCT文件按钮定义
<Commands package="guidQuickSwapSettingsExtensionPackage"> <Groups> <Group guid="guidQuickSwapSettingsExtensionPackageCmdSet" id="MyMenuGroup" priority="0x0600"> <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/> </Group> </Groups> <Buttons> <Button guid="guidQuickSwapSettingsExtensionPackageCmdSet" id="SwapSettingsCommandId" priority="0x0100" type="Button"> <Parent guid="guidQuickSwapSettingsExtensionPackageCmdSet" id="MyMenuGroup" /> <Icon guid="guidImages" id="bmpPic1" /> <CommandFlag>DynamicVisibility</CommandFlag> <Strings> <ButtonText>Invoke SwapSettingsCommand</ButtonText> </Strings> </Button> </Buttons> <Bitmaps> <Bitmap guid="guidImages" href="Resources\SwapSettingsCommand.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows, bmpPicStrikethrough"/> </Bitmaps> </Commands>
可见性约束
<VisibilityConstraints> <VisibilityItem guid="guidQuickSwapSettingsExtensionPackageCmdSet" id="SwapSettingsCommandId" context="UIContextGuid"/> </VisibilityConstraints>
GUID符号定义
<Symbols> <!-- This is the package guid. --> <GuidSymbol name="guidQuickSwapSettingsExtensionPackage" value="{d6069cfa-a050-490c-bf8a-5ddb780e204d}" /> <GuidSymbol name="UIContextGuid" value="{2cfcc18c-f6b5-418a-8531-2b8f3bf53cf5}" /> </Symbols>
实际问题表现:扩展启动后右键非config文件时菜单项不显示;但右键过config文件后,菜单项会永久显示在所有文件上,不会随选中文件切换而消失。需要菜单项仅在选中JSON文件时显示(或对其他文件禁用)。
问题出在ProvideUIContextRule的规则设计和UI上下文的自动加载逻辑上,以下是修复步骤:
1. 修正UI上下文规则,针对JSON文件匹配
将原有的.config后缀匹配改为.json,同时添加上下文失效条件,确保选中非JSON文件时UI上下文能自动关闭:
[ProvideUIContextRule(UIContextGuid, name: "Apply only to JSON files", expression: "DotJson", termNames: new[] { "DotJson" }, termValues: new[] { "HierSingleSelectionName:.json$" }, // 添加失效条件:当选中的文件不是.json时,上下文关闭 inverseExpression: "NotDotJson", inverseTermNames: new[] { "NotDotJson" }, inverseTermValues: new[] { "HierSingleSelectionName:^(?!.*\\.json$).*$" })]
2. 移除ProvideAutoLoad特性
ProvideAutoLoad会让包在UI上下文激活后一直保持加载状态,导致上下文规则无法正确重置。移除该特性,让VS按需加载包:
// 移除这一行 // [ProvideAutoLoad(UIContextGuid, PackageAutoLoadFlags.BackgroundLoad)]
3. 为命令组添加动态可见性约束
除了按钮的可见性,还需要给菜单组添加相同的上下文约束,避免组被永久激活:
在VSCT的VisibilityConstraints中添加组的约束:
<VisibilityConstraints> <VisibilityItem guid="guidQuickSwapSettingsExtensionPackageCmdSet" id="MyMenuGroup" context="UIContextGuid"/> <VisibilityItem guid="guidQuickSwapSettingsExtensionPackageCmdSet" id="SwapSettingsCommandId" context="UIContextGuid"/> </VisibilityConstraints>
4. 确保命令实现状态实时更新
如果你的命令类继承自BaseCommand,重写QueryStatus方法,主动检查当前选中文件的类型,确保命令状态实时更新:
protected override void QueryStatus(OleMenuCommand command) { // 获取当前选中的项目 var selectedItem = GetSelectedItem(); bool isJsonFile = selectedItem?.Name.EndsWith(".json", StringComparison.OrdinalIgnoreCase) ?? false; command.Visible = isJsonFile; command.Enabled = isJsonFile; }
关键原理
UI上下文规则默认是累积激活的,一旦匹配过一次就不会自动失效,必须通过inverseExpression明确指定失效条件。同时,ProvideAutoLoad会让包持续加载,干扰上下文的动态切换逻辑,移除后VS会根据上下文状态自动管理包的加载与卸载。
内容的提问来源于stack exchange,提问作者Dominick

