visual studio 插件开发(2) -- 动态菜单
这里所谓的动态菜单是指动态指定某些菜单是否显示是否可用,而不是指动态的创建一个新的菜单。就我现在所知,还没有找到通过代码生成菜单而不依赖于vsct的方法。在日常的情形中我们经常能够见到这些需求,比如当用户右击某个解决方案时希望显示“关闭该解决方案”,而在.cs文件上单击时候就不要显示这个选项。下面我们来实现这个需求。
方法一
新建一个带menu项目后,打开vsct文件并在Commands标签之后插入VisibilityConstraints标签。具体的内容如下图所示

你可能已经猜到了这句话的意思就是在没有解决方案打开的情况下,cmdidMyCommand按钮显示。一旦加载了解决方案这个按钮就不可见了。其中的context属性我们可以在vsx1中提到的vsshlids.h文件中找到。同时你也能看到其他你可以使用的context信息。

然后在button标签内插入CommandFlag标签,表明这是一个动态的菜单。

至此,你不需要多写一行c#代码就可以完成菜单的动态显示了。不过这种方法也存在一些缺陷,比如只能使用预定义的条件,比如UICONTEXT_NOSolution不能灵活的自定义其他的条件,而且这种方式只能控制菜单的隐藏与显示,如果我想disable这个菜单就办不到了。
方法二
这个方法弥补了上面说到的一些缺点。具体的做法如下:
- 和方法一一样,在button标签中添加CommandFlag标签,并设置为DynamicVisibility。
- 在创建代码
1 2 3 4 5 6 7 8 9 | OleMenuCommandService mcs = GetService( typeof (IMenuCommandService)) as OleMenuCommandService; if ( null != mcs) { // Create the command for the menu item. CommandID menuCommandID = new CommandID(GuidList.guidVSPackage1CmdSet, ( int )PkgCmdIDList.cmdidMyCommand); OleMenuCommand menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); menuItem.BeforeQueryStatus += new EventHandler(menuItem_BeforeQueryStatus); mcs.AddCommand(menuItem); } |
这里和原来创建的区别在于OleMenuCommand 对象。原来添加菜单的代码中使用的是MenuCommand对象,而这个OleMenuCommand 对象继承于MenuCommand并实现了一些特殊的功能和事件。比如说我们要使用到的BeforeQueryStatus事件。这个事件的意思就是每次vs进行状态更新的时候都会执行到这个方法,借助于这个机会你可以在该事件中对你的菜单进行动态的设置。事件方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | OleMenuCommand menuCommand = sender as OleMenuCommand; if (menuCommand != null ) { IntPtr hierarchyPtr, selectionContainerPtr; uint projectItemId; IVsMultiItemSelect mis; IVsMonitorSelection monitorSelection = (IVsMonitorSelection)Package.GetGlobalService( typeof (SVsShellMonitorSelection)); monitorSelection.GetCurrentSelection( out hierarchyPtr, out projectItemId, out mis, out selectionContainerPtr); IVsHierarchy hierarchy = Marshal.GetTypedObjectForIUnknown(hierarchyPtr, typeof (IVsHierarchy)) as IVsHierarchy; if (hierarchy != null ) { object value; hierarchy.GetProperty(projectItemId, ( int )__VSHPROPID.VSHPROPID_Name, out value); if (value != null && value.ToString().EndsWith( ".cs" )) { menuCommand.Visible = true ; } else { menuCommand.Visible = false ; } } } |
这里代码的具体意思不解释了,因为这篇文章的重点不在这里。这段代码的效果就是只有但你在.cs结尾的文件上右击菜单的时候才会见到菜单,否则就不会见到。
最后,由于vsx的动态加载机制,现在创建的动态菜单并不会显示。需要在package上面添加一个AutoLoad标签,让vs在启动的时候就自动加载我们的插件。这样动态菜单才能正确显示出来。
1 | [ProvideAutoLoad( "2c2dd76c-69d1-4889-b82f-b2f2e86c3eeb" )] |
这种方法相比较于上一种给我们提供了更大的灵活性和可控性。所以,在使用的时候肯定一般用第二种了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器