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

原文: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对象模型。

时间: 2024-10-27 08:07:06

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

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

原文:SSMS2008插件开发(1)--介绍 SSMS2008就是Microsoft Sql Server Management Studio 2008的简称.许多人叫做SQL2008或SQL SERVER2008是不准确的.SSMS是一个操作.管理SQL或SQL SERVER的UI工具. SSMS插件(SSMS add-in)是扩展SSMS功能的组件,比较著名的有SSMS Tools Pack.SQL Prompt.SQL Pretty Printer等.这些工具无一例外的让我们更加方便地使用

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

原文:SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍 由于开发SSMS2008插件是通过VS2008进行的,有必要先介绍一下VS2008的插件开发过程. 这次的目的是在VS2008的工具菜单中增加一个菜单项"Test Menu",该菜单项实现显示"Hello World"的功能. 1.打开"新建项目"对话框.在"项目类型"中选择"其他项目类型"--&g

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

原文:SSMS2008插件开发(3)--部署调试SSMS2008插件 上一次说到VS2008中的插件开发,最终结果插件是部署在VS2008中,现在我们将插件部署到SSMS2008(Microsoft Sql Server Management Studio 2008)中.可以参考一下这里. 打开上一次的项目MySSMSAddin,右"解决方案资源管理器"中右击该项目,选择"属性",进入该项目的属性设置界面.在"应用程序"选项卡中,将"程

自定义菜单创建接口

自定义菜单接口可实现多种类型按钮,如下: 1.click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互: 2.view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息. 3.scancode_push:扫码推事件用户点击按钮后,微信客户端将调起

自定义菜单查询接口

使用接口创建自定义菜单后,开发者还可使用接口查询自定义菜单的结构.另外请注意,在设置了个性化菜单后,使用本自定义菜单查询接口可以获取默认菜单和全部个性化菜单信息. 请求说明 http请求方式:GET https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN 返回说明(无个性化菜单时) 对应创建接口,正确的Json返回结果: {     "menu": {         "button"

微信公众平台自定义菜单及高级接口PHP SDK

本文介绍介绍微信公众平台自定义菜单及高级接口的PHP SDK及使用方法. 作者 方倍工作室 修正记录: 2014.05.03 v1.0 方倍工作室 http://www.cnblogs.com/txw1958/ SDK 源码: 1 /* 2 方倍工作室 http://www.cnblogs.com/txw1958/ 3 CopyRight 2014 www.doucube.com All Rights Reserved 4 */ 5 6 class class_weixin_adv 7 { 8

微信公众平台如何创建自定义菜单?

微信现在的功能越来越强大了,申请认证后的开发者能自定义菜单,用户直接点击微信界面下方的菜单,就能直接去到指定的页面,下面小编用[微信公众平台测试号]为大家演示一下怎么创建自定义菜单. 工具/原料 认证后的微信公众平台 微信公众平台切换开发者模式 方法/步骤 登录[微信公众平台],选择[功能]菜单下面的[高级功能],进入[开发模式]. 由于小编的微信公众平台还没通过认证,下面用[申请测试账户]为大家演示. 微信公众平台接口测试帐号申请,无需公众帐号.快速申请接口测试号,直接体验和测试公众平台所有高

微信自定义菜单中文乱码问题

文章转自 <微信自定义菜单中文乱码问题> 问题:微信自定义菜单开发者模式,菜单中文出现乱码 例: $menu = array( 'button' => array( array( 'type'=>'view', 'name'=>'百度', 'url'=>'https://www.baidu.com', ), ), ); 调用微信自定义菜单创建接口:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACC

php微信自定义菜单开发

微信自定义菜单需要有一个微信服务号,在开发之前需要获取access_token,获取方法很简单,登陆微信公众账号,进入开发者模式,就可以看到{开发者凭据}:下面AppId和AppSecret,开发者文档说明 : 接口调用请求说明 http请求方式: GEThttps://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET 参数说明 参数 是否必须 说明 grant