原文:SSMS2008插件开发(4)--自定义菜单

  打开上次的项目MySSMSAddin中的Connect类,发现该类继于了两个接口:IDTExtensibility2IDTCommandTarget,关于这两个接口的详细说明,请点击这两个接口转到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对象模型。

SSMS2008插件开发(4)--自定义菜单的更多相关文章

  1. SSMS2008插件开发(3)--部署调试SSMS2008插件

    原文:SSMS2008插件开发(3)--部署调试SSMS2008插件 上一次说到VS2008中的插件开发,最终结果插件是部署在VS2008中,现在我们将插件部署到SSMS2008(Microsoft ...

  2. SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍

    原文:SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍 由于开发SSMS2008插件是通过VS2008进行的,有必要先介绍一下VS2008的插件开 ...

  3. SSMS2008插件开发(1)--介绍

    原文:SSMS2008插件开发(1)--介绍 SSMS2008就是Microsoft Sql Server Management Studio 2008的简称.许多人叫做SQL2008或SQL SER ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(74)-微信公众平台开发-自定义菜单

    系列目录 引言 1.如果不借用Senparc.Weixin SDK自定义菜单,编码起来,工作量是非常之大 2.但是借助SDK似乎一切都是简单得不要不要的 3.自定义菜单无需要建立数据库表 4.自定义菜 ...

  5. 用c#开发微信(5)自定义菜单设置工具 (在线创建)

    读目录 1 使用 2 原理 3. 错误 上次写了<用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)>,有园友问到如何创建菜单的问题,今天就介绍下 ...

  6. 《C#微信开发系列(2)-自定义菜单管理》

    2.0自定义菜单管理 ①接口说明 微信服务号聊天窗口下面的菜单项(有的公众号有启用有的则没有),这个可以在编辑模式简单配置,也可以在开发模式代码配置.微信公众平台开发者文档:微信公众号开发平台创建自定 ...

  7. 微信公众平台自定义菜单新增扫一扫、发图片、发位置 LBS运作更便捷

    今天微信公众平台发布更新,自定义菜单新增扫一扫.发图片.发送位置等功能,这对于有意挖掘微信LBS服务的运营者来说更便捷了,订阅号不用返回微信界面就能扫图.发送图片.调用地理位置,用户体验更友好,自然也 ...

  8. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  9. 微信公众平台创建自定义菜单中文编码导致system error

    创建包含了中文的自定义菜单时总是返回{"errcode":-1,"errmsg":"system error"},要将编码方式设置为UTF- ...

随机推荐

  1. Youtube最佳Red5 官方视频下载指南,字幕【亲测成功】

    前言 最近在研究Red5 流媒体服务框架,官网上的信息足以让一个新手入门 有官方參考手冊 -- 高速了解red5的相关信息 有Red5 on Stackoverflow  -- 在上面能够提问或者回答 ...

  2. 备忘录模式设计模式入门Memento

    //备忘录模式定义: //在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态. //这样以后就能够将该对象恢复到原先保存的状态 //实例:測试两种方案.两种方案在第一阶段的过程 ...

  3. Eclipse正在使用Ant扑灭Android数据包错误的解决方案 – Perhaps JAVA_HOME does not point to the JDK

    问题描写叙述: 在Eclipse中执行ant批量打包工具出错,日志信息例如以下: D:\Android\android-sdk-windows\tools\ant\build.xml:601: The ...

  4. Cocos2d-3x:vs2012项目开关android项目需要注意的地方

    http://www.cocoachina.com/bbs/read.php?tid=194668 先依照这个文章导入库到vs项目. 在vs项目的sceen类的里加入 #include "c ...

  5. RPM安装包-Spec文件參数具体解释与演示样例分析

    spec文件是整个RPM包建立过程的中心,它的作用就如同编译程序时的Makefile文件. 1.Spec文件參数 spec文件包括建立一个RPM包必需的信息,包括哪些文件是包的一部分以及它们安装在哪个 ...

  6. Hybrid----U采用IWebView演出PDF和其他文件

    App如果你需要显示pdf.word档,在这个时候有没有其他控制,比UIWebView更适合,这是高度抽象的技术细节,可以非常easy采用 UIWebView打开文件类型列表 watermark/2/ ...

  7. 使用 CodeIgniter 框架快速开发 PHP 应用(三)

    原文:使用 CodeIgniter 框架快速开发 PHP 应用(三) 分析网站结构既然我们已经安装 CI ,我们开始了解它如何工作.读者已经知道 CI 实现了MVC式样. 通过对目录和文件的内容进行分 ...

  8. Spring4 SpringMVC Hibernate4 Freemaker 集成示例

    变更更正(2014-05-30 13:47:22):一些IDE在web.xml我们会报告这个错误: cvc-complex-type.2.4.a: Invalid content was found ...

  9. C/C++各种类型int、long、double、char表示范围(最大和最小)

    #include<iostream> #include<string> #include <limits> using namespace std; int mai ...

  10. c++的string容器

    c++相比c的一个好处就是实现了很多的容器和泛型算法,使得程序员的工作得到了很大的化简.其中一个很有用的泛型容器就是string.string是一个类,是一个在STL里边实现好了的类,由于他的很多功能 ...