visual studio外掛開發-Menu

2022-10-29 09:00:46

工欲善其事,必先利其器,作為程式設計師我們很大部分時間在和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!!!

關注公眾號一起學習