• 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">
        
        <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">
        
        <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>NewButtonText>
          <CommandName>NewCommandName>
        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 TestCommandButtonText>
        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>DynamicVisibilityCommandFlag>
        <Icon guid="guidImages" id="bmpPic1" />
        <Strings>
          <ButtonText>Invoke TestCommandButtonText>
        Strings>
    Button>

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

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

    image
    image

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

    总结

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

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

    Enjoy!!!

    关注公众号一起学习

     

  • 相关阅读:
    202206-2 CCF 寻宝!大冒险! (简单模拟 满分题解)
    283. 移动零
    【C++初阶】前言——C++的发展简述及学习方法分享
    spark on yarn-cluster在生产环境 部署 spark 任务, 同时支持读取外部可配置化文件
    用Promise发起请求,失败后再次请求,几次后不再执行
    vcruntime140.dll丢失的解决方法?教你如何修复好dll文件
    python对于grpc的简单操作(一)
    第二章 C API稳定性
    “鼓浪屿元宇宙”,能否成为中国文旅产业的“升级样本”
    跟着GPT学设计模式之观察者模式
  • 原文地址:https://www.cnblogs.com/yudongdong/p/16837986.html