visual studio 插件开发(2) -- 动态菜单

      这里所谓的动态菜单是指动态指定某些菜单是否显示是否可用,而不是指动态的创建一个新的菜单。就我现在所知,还没有找到通过代码生成菜单而不依赖于vsct的方法。在日常的情形中我们经常能够见到这些需求,比如当用户右击某个解决方案时希望显示“关闭该解决方案”,而在.cs文件上单击时候就不要显示这个选项。下面我们来实现这个需求。
 
方法一
 
     新建一个带menu项目后,打开vsct文件并在Commands标签之后插入VisibilityConstraints标签。具体的内容如下图所示
 
你可能已经猜到了这句话的意思就是在没有解决方案打开的情况下,cmdidMyCommand按钮显示。一旦加载了解决方案这个按钮就不可见了。其中的context属性我们可以在vsx1中提到的vsshlids.h文件中找到。同时你也能看到其他你可以使用的context信息。
 
然后在button标签内插入CommandFlag标签,表明这是一个动态的菜单。
 
至此,你不需要多写一行c#代码就可以完成菜单的动态显示了。不过这种方法也存在一些缺陷,比如只能使用预定义的条件,比如UICONTEXT_NOSolution不能灵活的自定义其他的条件,而且这种方式只能控制菜单的隐藏与显示,如果我想disable这个菜单就办不到了。
 
方法二
 
这个方法弥补了上面说到的一些缺点。具体的做法如下:
  • 和方法一一样,在button标签中添加CommandFlag标签,并设置为DynamicVisibility。
  • 在创建代码
            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进行状态更新的时候都会执行到这个方法,借助于这个机会你可以在该事件中对你的菜单进行动态的设置。事件方法如下:
 
 
  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在启动的时候就自动加载我们的插件。这样动态菜单才能正确显示出来。
 
 [ProvideAutoLoad("2c2dd76c-69d1-4889-b82f-b2f2e86c3eeb")]

  

这种方法相比较于上一种给我们提供了更大的灵活性和可控性。所以,在使用的时候肯定一般用第二种了。
 
 
 
posted @ 2011-12-09 11:21  qianlifeng  阅读(2913)  评论(0编辑  收藏  举报