打開上次的項目MySSMSAddin中的Connect類,發現該類繼于了兩個接口: IDTExtensibility2 和 IDTCommandTarget ,關于這兩個接口的詳細說明,請點擊這兩個接口轉到MSDN。
IDTExtensibility2接口有2個重要的方法:OnConnection和OnDisconnection。OnConnection表示當(宿主)SSMS加載外接程序的時候調用此接口,可以在此方法中做些初始化的工作,如加載菜單等;OnDisconnection方法表示當SSMS卸載外接程序的時候調用此方法,可以在此方法中做些清理工作。
OnConnection方法的代碼如下:
/// <summary> /// 實現 IDTExtensibility2 接口的 OnConnection 方法。接收正在加載外接程序的通知。 /// </summary> /// <param term='application'>宿主應用程序的根對象。</param> /// <param term='connectMode'>描述外接程序的加載方式。</param> /// <param term='addInInst'>表示此外接程序的對象。</param> /// <seealso class='IDTExtensibility2' /> public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)ServiceCache.ExtensibilityModel; _addInInstance = (AddIn)addInInst; if (connectMode == ext_ConnectMode.ext_cm_Startup) { object []contextGUIDS = new object[] { }; Commands2 commands = (Commands2)_applicationObject.Commands; string toolsMenuName; try { string resourceName; ResourceManager resourceManager = new ResourceManager("MySSMSAddin.CommandBar", Assembly.GetExecutingAssembly()); CultureInfo cultureInfo = new CultureInfo(_applicationObject.LocaleID); if(cultureInfo.TwoLetterISOLanguageName == "zh") { System.Globalization.CultureInfo parentCultureInfo = cultureInfo.Parent; resourceName = String.Concat(parentCultureInfo.Name, "Tools"); } else { resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools"); } toolsMenuName = resourceManager.GetString(resourceName); } catch { toolsMenuName = "Tools"; } Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"]; CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName]; CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl; try { Command command = commands.AddNamedCommand2(_addInInstance, "MySSMSAddin", "Test Menu", "Executes the command for MySSMSAddin", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton); if((command != null) && (toolsPopup != null)) { command.AddControl(toolsPopup.CommandBar, 1); } } catch(System.ArgumentException) { //如果出現此異常,原因很可能是由于具有該名稱的命令 // 已存在。如果確實如此,則無需重新創建此命令,并且 // 可以放心忽略此異常。 } } }該方法有4個參數:application表示宿主對象,這里指SSMS本身(在VS2008中表示DTE);connectMode表示外接程序的加載方式,在SSMS中此值總是ext_cm_Startup;addInInst表示插件本身,這里指我們的MySSMSAddin.Connect;custom 不知道什么作用(MSDN中的解釋是 一個空數組,可用來傳遞在外接程序中使用的特定于主機的數據)。
DTE對象是操作SSMS的核心對象,包括菜單、工具欄、文檔、工具箱、錯誤列表等都通過該對象獲取。所以,在OnConnect方法的一開始,就取得DTE對象,代碼如下:
_applicationObject = (DTE2)ServiceCache.ExtensibilityModel; _addInInstance = (AddIn)addInInst;上面代碼的第2句獲取插件的實例。
_applicationObject.Commands(DTE.Commands)表示SSMS中所有的命令項;而_applicationObject.CommandBars(DTE.CommandBars)包含所有的菜單項,例如文件菜單、工具菜單以及快捷菜單。要想在“工具”菜單中增加一個命令,必須先找到“工具”菜單,由于不同語言版本菜單的名稱不一樣,所以首先通過資源文件(CommandBar.resx)找到工具菜單的名稱,然后通過名稱在主菜單(MenuBar)中找到“工具”菜單,再在“工具”菜單中增加菜單項。
//獲取所有的菜單命令 Commands2 commands = (Commands2)_applicationObject.Commands;
在資源文件中查找“工具”菜單的名稱:
try { string resourceName; ResourceManager resourceManager = new ResourceManager("MySSMSAddin.CommandBar", Assembly.GetExecutingAssembly()); CultureInfo cultureInfo = new CultureInfo(_applicationObject.LocaleID); if (cultureInfo.TwoLetterISOLanguageName == "zh") { System.Globalization.CultureInfo parentCultureInfo = cultureInfo.Parent; resourceName = String.Concat(parentCultureInfo.Name, "Tools"); } else { resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools"); } toolsMenuName = resourceManager.GetString(resourceName); } catch { toolsMenuName = "Tools"; }
獲取主菜單,并在主菜單中獲取“工具”菜單的引用:
Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"]; CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName]; CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;
通過 Commands.AddNamedCommand2 增加一個菜單命令:
Command command = commands.AddNamedCommand2(_addInInstance , "MySSMSAddin" , "Test Menu" , "Executes the command for MySSMSAddin" , true , 59 , ref contextGUIDS , (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled , (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton );第1個參數為要添加菜單命令的插件;
第2個參數為菜單命令的名稱, 該方法會自動在給定的名稱前加上Progid前綴,在這里這個前綴為命名空間.類名(MySSMSAddin.Connect) ;
第3個參數為菜單顯示的文本;
第4個參數為菜單的提示信息;
第5個參數為true表示使用Office圖標,false表示使用其他來源的圖標;怎么使用自定義圖標,以后再說。
第6個參數圖標ID;
第7個參數確定哪些環境上下文(即調試模式、設計模式等)啟用此命令;
第8個參數指示當指定上下文不存在時,此命令是不可見還是禁用狀態等信息;
第9個參數確定菜單的顯示風格,例如是光文字還是光圖標,或者兩者都顯示。
將新增的菜單加入到“工具”菜單中,作為“工具”菜單的子菜單:
command.AddControl(toolsPopup.CommandBar, 1);
以上步驟僅僅在“工具”菜單中增加了一個菜單命令,但是單擊該命令并沒有任何響應,要響應自定義菜單,還需要實現IDTCommandTarget接口的兩個方法:QueryStatus和Exec。QueryStatus方法 返回指定命名命令的當前狀態(啟用、禁用、隱藏等) ;Exec方法用于 執行指定的命名命令 。
返回命令狀態:
/// <summary> /// 實現 IDTCommandTarget 接口的 QueryStatus 方法。此方法在更新該命令的可用性時調用 /// </summary> /// <param term='commandName'>要確定其狀態的命令的名稱。</param> /// <param term='neededText'>該命令所需的文本。</param> /// <param term='status'>該命令在用戶界面中的狀態。</param> /// <param term='commandText'>neededText 參數所要求的文本。</param> /// <seealso class='Exec' /> public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText) { if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone) { if (commandName == "MySSMSAddin.Connect.MySSMSAddin") { status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled; return; } } }注意上述代碼中的命令名稱 MySSMSAddin.Connect.MySSMSAddin ,我們在指定命令名稱的時候,只指定為MySSMSAddin,怎么這里的內容變多了呢?因為前面說過,使用Commands.AddNamedCommand2方法時,會在名稱前面自動加上Progid。
響應菜單事件:
/// <summary> /// 實現 IDTCommandTarget 接口的 Exec 方法。此方法在調用該命令時調用。 /// </summary> /// <param term='commandName'>要執行的命令的名稱。</param> /// <param term='executeOption'>描述該命令應如何運行。</param> /// <param term='varIn'>從調用方傳遞到命令處理程序的參數。</param> /// <param term='varOut'>從命令處理程序傳遞到調用方的參數。</param> /// <param term='handled'>通知調用方此命令是否已被處理。</param> /// <seealso class='Exec' /> public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled) { handled = false; if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault) { if (commandName == "MySSMSAddin.Connect.MySSMSAddin") { System.Windows.Forms.MessageBox.Show("Hello World"); handled = true; return; } } }
以上添加菜單的方法比較復雜,而且是COM時代的用法,下面的方法也許更適合C#, 可以參考這里 :
/// <summary> /// 實現 IDTExtensibility2 接口的 OnConnection 方法。接收正在加載外接程序的通知。 /// </summary> /// <param term='application'>宿主應用程序的根對象。</param> /// <param term='connectMode'>描述外接程序的加載方式。</param> /// <param term='addInInst'>表示此外接程序的對象。</param> /// <seealso class='IDTExtensibility2' /> public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)ServiceCache.ExtensibilityModel; _addInInstance = (AddIn)addInInst; if (connectMode == ext_ConnectMode.ext_cm_Startup) { object[] contextGUIDS = new object[] { }; Commands2 commands = (Commands2)_applicationObject.Commands; string toolsMenuName; try { string resourceName; ResourceManager resourceManager = new ResourceManager("MySSMSAddin.CommandBar", Assembly.GetExecutingAssembly()); CultureInfo cultureInfo = new CultureInfo(_applicationObject.LocaleID); if (cultureInfo.TwoLetterISOLanguageName == "zh") { System.Globalization.CultureInfo parentCultureInfo = cultureInfo.Parent; resourceName = String.Concat(parentCultureInfo.Name, "Tools"); } else { resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools"); } toolsMenuName = resourceManager.GetString(resourceName); } catch { toolsMenuName = "Tools"; } Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"]; CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName]; CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl; try { //例如 CommandBarPopup 來添加菜單 CommandBarControl command = toolsPopup.Controls.Add(MsoControlType.msoControlButton, 1, "", 1, true); command.Tag = "測試"; command.Caption = "另一種菜單"; //菜單標題 command.TooltipText = "測試另一種添加菜單的方法"; //提示 //獲取 command 的事件,注意:commandHandler不能在方法中定義,如果這樣就不能響應事件 // 必須要定義為類級變量,不知道為什么必須這樣。而且VSTO編程中也是這樣。 commandHandler = (CommandBarEvents)_applicationObject.DTE.Events.get_CommandBarEvents(command); commandHandler.Click += new _dispCommandBarControlEvents_ClickEventHandler(commandHandler_Click); } catch (System.ArgumentException) { } } } //添加菜單的事件對象 CommandBarEvents commandHandler; /// <summary> /// 菜單響應事件 /// </summary> /// <param name="CommandBarControl"></param> /// <param name="Handled"></param> /// <param name="CancelDefault"></param> void commandHandler_Click(object CommandBarControl, ref bool Handled, ref bool CancelDefault) { MessageBox.Show("hello"); }
還有另外一種綁定菜單事件的方法,核心代碼如下
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)ServiceCache.ExtensibilityModel; _addInInstance = (AddIn)addInInst; if (connectMode == ext_ConnectMode.ext_cm_Startup) { ... try { ... } catch { toolsMenuName = "Tools"; } ... try { //利用 CommandBarPopup 來添加菜單, // 相對于類型為 MsoControlType.msoControlButton 的菜單,其類型為CommandBarButton // 利用 CommandBarButton 的Click事件,來響應命令 command = toolsPopup.Controls.Add(MsoControlType.msoControlButton, 1, "", 1, true) as CommandBarButton; command.Tag = "測試"; command.Caption = "另一種菜單"; //菜單標題 command.TooltipText = "測試另一種添加菜單的方法"; //提示 command.Click += new _CommandBarButtonEvents_ClickEventHandler(command_Click); } catch (System.ArgumentException) { } } } //必須定義為類級變量 CommandBarButton command; //響應命令 void command_Click(CommandBarButton Ctrl, ref bool CancelDefault) { MessageBox.Show("Hello"); }后兩種增加菜單的方法都有一個共同缺點,一個是菜單本身要定義為類級,另一個是響應事件對象要定義為類級,而且都不知道怎么查詢命令的可用狀態。
下一次介紹SSMS及DTE對象模型。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
