8

visual studio插件开发-Menu - 俞正东

 1 year ago
source link: https://www.cnblogs.com/yudongdong/p/16837986.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

visual studio插件开发-Menu

工欲善其事,必先利其器,作为程序员我们很大部分时间在和ide打交道,好的插件可以大大提高我们的编程效率,我开发过几个vs插件来解决一键生成dbmodels,快速部署到服务器,总结下来最关键的还是对于Menu这块的扩展,因为这是插件功能的最常见的入口之一,下面给大家介绍vs插件各种menu的扩展

这里我使用vs2022版本,要开发vs插件的话,需要vs安装插件开发模块

打开vs 然后点击 工具 -> 获取工具和功能

image

然后勾选Visual Studio扩展开发

image

安装好之后,打开vs就可以选择到 vsix project 模板了

image

image

我们利用vsix project模板创建一个插件工程

image

image

image

image

  • MenuDemoVSIXPackage.cs(是插件的入口类)
  • source.extension.vsixmanifest(插件的描述,比如版本,说明等描述性配置的地方)

空的vsix project就创建成功了,我们添加一个command(菜单操作)

image

image

image

image

创建了一个Command会新增下面3个

  • 一个png (图标)
  • 一个vsct (不管几个Command都只会有一个这个文件,包含所有自定义菜单的配置)
  • TestCommand.cs (自定义菜单的命令,点击菜单的执行操作逻辑在里面)
image

image

点击启动这个插件,会打开一个有插件环境的vs(隔离的)

会看到我们的Command名称:Invoke TestCommand按钮在vs的[工具]这个菜单里面, 点击它会出一个弹框,如下

image

好了,以上完成初体验后,回到本文要重点介绍:vs的Menu扩展

vs的Menu扩展

上面我们说到 vsct文件,我们的按钮是展示在Vs哪种类型的Menu下,就是在这个文件定义的,我们一起看下这个vsct文件,关键部分我都用不同颜色来高亮显示

image

image

CommandTable 表示与VSPackage关联的所有命令、菜单组和菜单。
Extern 表示引用外部.h文件,最终会与.vsct文件合并的
  • stdidcmd.h
  • vsshlids.h

VSCT 编译器能使用 C++ 宏和预处理,通过extern引入头文件,比如vsshlids.h vsshlids.h 头文件位于

{VS安装目录}\VSSDK\VisualStudioIntegration\Common\Inc,

例如我的目录是

C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VSSDK\VisualStudioIntegration\Common\Inc

vsct 文件中有用到宏 IDM_VS_MENU_TOOLS = 0x0005,

它表示 VS 上的 Tools 菜单的ID,这个宏即位于 vsshlids.h 头文件中。

如果不引入这个头文件,那么就得写0x0005,导致可读性很差和难维护!

image

image

Commands 表示可以执行命令的集合。每个命令都有以下四个子元素:
  • Menus 是菜单/工具栏的集合。菜单是Commands的容器。
  • Groups 决定菜单的位置
  • Buttons 表示命令按钮/菜单项
  • Bitmaps 按钮/菜单项的图标配置
CommandPlacements 指示各个命令应位于VSPackage菜单中的其他位置。
Symbols 包含包中所有命令的符号名和GUID, ID。
KeyBindings 快捷键指定 例如Ctrl+S。

以上vsct的xml scheme 的详细说明在这里有文档

https://github.com/MicrosoftDocs/visualstudio-docs/blob/main/docs/extensibility/internals/designing-xml-command-table-dot-vsct-files.md

<Groups>
  <Group guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup" priority="0x0600">
    <!-- 这个guid和id决定了菜单的位置 -->
    <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
  </Group>
</Groups>

如果想要展示在vs的下面这些菜单里面,直接可以用上面的方式 修改id就可以了

image

image

id的定义都在vsshlids.h 头文件,常用的如下

-》vs的最上面一排菜单
#define IDM_VS_MENU_FILE              0x0080
#define IDM_VS_MENU_EDIT              0x0081
#define IDM_VS_MENU_VIEW              0x0082
#define IDM_VS_MENU_PROJECT           0x0083
#define IDM_VS_MENU_TOOLS             0x0085
#define IDM_VS_MENU_WINDOW            0x0086
#define IDM_VS_MENU_ADDINS            0x0087
#define IDM_VS_MENU_HELP              0x0088
#define IDM_VS_MENU_DEBUG             0x0089
#define IDM_VS_MENU_FORMAT            0x008A
#define IDM_VS_MENU_ALLMACROS         0x008B
#define IDM_VS_MENU_BUILD             0x008C
#define IDM_VS_MENU_CONTEXTMENUS      0x008D
#define IDG_VS_MENU_CONTEXTMENUS      0x008E
#define IDM_VS_MENU_REFACTORING       0x008f
#define IDM_VS_MENU_COMMUNITY         0x0090
#define IDM_VS_MENU_EXTENSIONS        0x0091
-》 工程文件右键菜单 对应上图的13
#define IDM_VS_CTXT_PROJNODE          0x0402
-》代码窗口的右键菜单操作 对应上图的14
#define IDM_VS_CTXT_CODEWIN           0x040D
-》解决方案的右键菜单操作 对应上图的15
#define IDM_VS_CTXT_SOLNNODE          0x0413
-》 某个文件的右键菜单 这个也经常用
#define IDM_VS_CTXT_ITEMNODE          0x0430

各个含义说明也可以参考文档

https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/guids-and-ids-of-visual-studio-menus?view=vs-2022

比如我把上面的demo改成这样

<Groups>
  <Group guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup" priority="0x0600">
    <!-- 工程文件右键菜单 -->
    <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE"/>
  </Group>
</Groups>
image

image

改成这样就会显示在代码窗口的右键菜单中

<Groups>
  <Group guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup" priority="0x0600">
    <!-- 代码窗口的右键菜单操作 -->
    <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_CODEWIN"/>
  </Group>
</Groups>
image

image

所以一级菜单只需要添加一个Group 并且设置该Group的Parent为已知的定义ID即可

这里需要添加Menu了 且 一级菜单项要定义为Menu而不是Button!!

先新建一个group1以**右键菜单为parent(已知定义ID)**,以group1为parent,再定义一个group2以一级菜单Menu为parent,再将二级菜单项定义为Button并以group2为parent

有点绕吧,比如我要在工程文件的右键菜单 添加一个二级菜单,像下面这样子

image

image

  1. 在Groups节点下新建一个group:MyMenuGroup1 以工程右键菜单为parent
<Group guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup1" priority="0x0600">
    <!--定义在头文件的已知定义ID -->
    <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE"/>
</Group>
  1. 在Menus节点下新建一个menu:MyMenu,以上面的MyMenuGroup1位parent

<Menus>
  <Menu guid ="guidMenuDemoVSIXPackageCmdSet" id="MyMenu" priority="0x3110" type="Menu">
    <Parent guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup1"/>
    <Strings>
      <ButtonText>New</ButtonText>
      <CommandName>New</CommandName>
    </Strings>
  </Menu>
</Menus>
  1. 再创建一个group:MyMenuGroup2 以上面的MyMenu为parent
<Group guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup2" priority="0x0600">
    <Parent guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenu"/>
</Group>
  1. 创建Button以MyMenuGroup2为parent
<Buttons>
  <Button guid="guidMenuDemoVSIXPackageCmdSet" id="TestCommandId" priority="0x0100" type="Button">
    <Parent guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup2" />
    <Icon guid="guidImages" id="bmpPic1" />
    <Strings>
      <ButtonText>Invoke TestCommand</ButtonText>
    </Strings>
  </Button>
</Buttons>

完整定义:

image

如果想要同时显示在多个地方咋整

比如 我既要显示在工程右键菜单里面,又要显示在普通文件的右键菜单,又要显示在代码右键菜单

这里就用到上面提到的 CommandPlacements

还是以上面的例子,这时候第一步的group1:MyMenuGroup2的parent就不能填了

而是要添加CommandPlacements ,id要填 MyMenuGroup2 ,Parent填具体ID

<CommandPlacements>
    <CommandPlacement guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup1" priority="0x0000">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE"/>
    </CommandPlacement>
    <CommandPlacement guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup1" priority="0x0000">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE" />
    </CommandPlacement>
    <CommandPlacement guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup1" priority="0x0000">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_CODEWIN" />
    </CommandPlacement>
</CommandPlacements>
image

image

效果如下:

image

image

怎样动态展示菜单

比如 ,非json文件的就不展示

image

是json文件的才展示

image

在Button的增加 DynamicVisibility


 <Button guid="guidMenuDemoVSIXPackageCmdSet" id="TestCommandId" priority="0x0100" type="Button">
    <Parent guid="guidMenuDemoVSIXPackageCmdSet" id="MyMenuGroup2" />
    <!--这个 -->
    <CommandFlag>DynamicVisibility</CommandFlag>
    <Icon guid="guidImages" id="bmpPic1" />
    <Strings>
      <ButtonText>Invoke TestCommand</ButtonText>
    </Strings>
</Button>

让VsPackage随着项目启动后就立即加载,不然动态判断逻辑无法提前指定

image

修改Command的初始化方法,拿到DTE,很多功能点需要用到它里面的接口,比如拿到当前选择的item

image

image

然后再初始化Menu的时候指定BeforeQueryStatus的逻辑为后缀为json才展示

image

我觉得对于visual studio中如何用插件来扩展menu 大概了解上面几点就差不多了,希望能帮助到你

有个好消息和大家分享,昨天收到通知我当选了本届的微软MVP,以后会带给大家更多的技术分享~~~

Enjoy!!!

关注公众号一起学习

472365-20210606154607111-1839147492.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK