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

环境准备

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

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

然后勾选Visual Studio扩展开发

小试牛刀

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

image

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

image

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

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

image

image

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

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

image

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

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

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

vs的Menu扩展

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

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
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

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

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

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

image

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

二级菜单

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

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

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

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>

完整定义:

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

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

这里就用到上面提到的 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

怎样动态展示菜单

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

是json文件的才展示

在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随着项目启动后就立即加载,不然动态判断逻辑无法提前指定

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

image

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

总结

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

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

Enjoy!!!

关注公众号一起学习

visual studio插件开发-Menu的更多相关文章

  1. Visual Studio插件开发基础

    Visual Studio插件主要有两种:Add-in 和 VSX(Visual Studio eXtensibility) 两者区别可参考这篇文章:Visual Studio Extensions ...

  2. visual studio 插件开发

    插件的定义 所谓插件,就是根据平台接口开发的第三方程序.第一次听到这个名词很是不了解,听了解释也不是很明白,那我们来举个例子,比如说一辆房车,现在里面只有基本的一些设施,但是你现在想在顶部有一个晒太阳 ...

  3. Visual Studio 插件开发资源

    微软官方MSDN 官方MSDN永远是最大而全的电子字典Visual Studio Software Development Kit ,不过它的资料虽然详细,但没有一定的基础的话直接使用它的话有点无从入 ...

  4. visual studio插件开发dll类库免加全局缓存处理办法

    1.卸载VSIXProject 2.然后编辑*.csproj 修改如下: 3.重新加载项目 编辑source.extension.vsixmanifest 添加资产: 完事后,直接安装VISX就可以了

  5. 【小试插件开发】给Visual Studio装上自己定制的功能来提高代码调试效率

    背后的故事 随着项目需求的逐步增加,后端开发框架在我手上也慢慢重构为组件开发模式,整体结构类似于NopCommence.在这种结构中,每个组件所在的类库项目其实是生成到网站项目里指定的一个目录的,然后 ...

  6. 微软正式发布Visual Studio 2013 Update 3 (2013.3) RTM

    昨天微软的Visual Studio 2013 Update 3(Visual Studio 2013.3)正式发布(RTM)了,做为微软认证金牌合作的葡萄城控件,我们组织力量第一时间进行翻译.分享给 ...

  7. (英文版)使用Visual Studio 2015 编写 MASM 汇编程序!

    原文地址:http://kipirvine.com/asm/gettingStartedVS2015/index.htm#CreatingProject Getting Started with MA ...

  8. Visual Studio创建跨平台移动应用_02.Cordova Extension

    1简介 本章节是关于Visual Studio Tools for Apache Cordova的,目前此产品只发布了预览版.Visual Studio for Apache Cordova帮助熟悉V ...

  9. 微软正式公布Visual Studio 2013 Update 3 (2013.3) RTM

     昨天微软的Visual Studio 2013 Update 3(Visual Studio 2013.3)正式公布(RTM)了,做为微软认证金牌合作的葡萄城控件,我们组织力量第一时间进行翻译. ...

随机推荐

  1. FormData 和表单元素(form)的区别

    Form 元素 <form>元素表示文档中的一个区域,此区域包含交互控件,用于向 Web 服务器提交信息(文件.字符).下面称之为表单元素或表单. 要向 Web 服务器提交信息,我们必须要 ...

  2. feign的fallback操作

    Fallback可以帮助我们在使用Feign去调用另外一个服务时,如果出现了问题,走服务降级,返回一个错误数据,避免功能因为一个服务出现问题,全部失效. 依赖: <dependency> ...

  3. 制作离线yum源

    互联网上操作 1.安装所需依赖环境和软件包 1.1安装命令 yum install yum-utils createrepo 1.2各软件包功能 createrepo :生成yum 源各软件之间的依赖 ...

  4. DL基础:cs231n assignment 1

    cs231n assignment 1 20210804 - 20210808. 目录 cs231n assignment 1 总结 KNN 思想 cross-validation 编程细节 SVM ...

  5. 【HTML】学习路径1-网页基本结构-标签基本语法

    本系列将学习最基础的web前端知识: HTML---CSS---JavaScripts---jQuery 四大部分学习完以后再进入到JavaWeb的知识.(后端) 然后再学习SpringBoot技术. ...

  6. C++ 对于函数名的操作,函数名本身和取*以及取&的区别

    void TestFunc() { } int _tmain(int argc, _TCHAR* argv[]) { cout<<TestFunc<<endl; cout< ...

  7. C与C++有什么区别

    C是一个结构化语言,它的侧重点在于算法和数据结构.对语言本身而言,C是C++的一个子集. C程序的设计首要考虑的是如何通过一个过程,对输入进行运算处理,得到输出. 对于C++,首要考虑的是如何构造一个 ...

  8. Docker容器网络基础总结

    ifconfig 之 docker0 基于Linux的虚拟网桥(通用网络设备的抽象) 虚拟网桥特点: 1. 可以设置IP地址 2.相当于拥有一个隐藏的虚拟网卡 docker0 的地址划分 IP: 17 ...

  9. 【FAQ】接入华为应用内支付服务常见问题解答

    HMS Core应用内支付服务(In-App Purchases,IAP)为应用提供便捷的应用内支付体验和简便的接入流程.开发者的应用集成IAP SDK后,调用IAP SDK接口,启动IAP收银台,即 ...

  10. 跨语言调用C#代码的新方式-DllExport

    简介 上一篇文章使用C#编写一个.NET分析器文章发布以后,很多小伙伴都对最新的NativeAOT函数导出比较感兴趣,今天故写一篇短文来介绍一下如何使用它. 在以前,如果有其他语言需要调用C#编写的库 ...