zoukankan      html  css  js  c++  java
  • Excel 菜单系统

    Excel 菜单系统

    在开始Excel开发之前,需要把架子搭起来。最直接的那就是Excel里面的菜单了,他向用户直观的展现了我们的插件具有哪些功能。菜单出来之后我们就可以实现里面的事件和功能了。Excel菜单有两种形式,一种是Excel 2003及之前的传统菜单样式,一种是Excel 2007及之后的Ribbon菜单。本文首先讲解Excel 2007中菜单的创建,包括使用Visual Studio可视化设计菜单,菜单的RibbonXml配置,然后讲解如何在Excel 2003中创建自定义菜单。最后演示如何使用SharedAddin技术将两者结合起来,即在2003版本中显示原始的菜单样式,在以2003上版本中动态加载Ribbon菜单,从而达到版本的兼容。

    一 Excel 的Ribbon菜单及Ribbon Xml文件

    要演示菜单的创建,我们首先创建一个VSTO程序,如图在VS中创建一个Excel外接程序:

    Create VSTO Project

    然后接下来,添加项,添加一个Ribbon菜单:

    Create Ribbon Menu

    在创建菜单之前,需要明确我们的插件具有哪些模块。这里为了演示如何创建菜单以及后面的功能点,我们的插件打算做四个功能点。 首先是财经模块,包括从一些开放的财经API如新浪财经API,雅虎API中获取实时或者历史行情数据;地图模块,包括地图显示,地址检索,专题制图等;天气模块,获取天气,天气保表;系统模块,包括登录,帮助,关于模块等。确定好功能点之后,就可以开始创建菜单了。

    添加了Ribbon菜单之后,就可以打开ToolBox开始设计了,如下图。下面介绍各个菜单项的功能及设计要点。

    基本控件

    2.1 Tab控件

    VSTO Design Interface

    首先介绍的是RibbonTab控件,它是所有控件的容器,当我们添加一个Ribbon菜单的时候,VS默认会给我们创建一个RibbonTab控件,一个VSTO项目可以创建多个Tab控件,您需要做的只是从ToolBox中推拽一个Tab至设计界面上即可。Tab控件有一些属性。

    VSTO Tab control Property

    其中比较重要的属性为ControlIdType,如果类型是Office的话,他会嵌入到Office内置系统的 Tab页中,而不是默认的新创建一个以Label为名称的Tab页。下图是ControlIdType为Office的效果,如果PositionType属性设置为Default的话,会出现在Office加载项 这个标签页中。

    TabControlTypeOffice

    如果将ControlIdType设置为Custom,则在界面上会显示以Label命名的一个新的Tab页,这里将Label改为歪歪插件。效果如下图:

    TabControlTypeCustom

    一般的,我们会采用Custom的方式,让我们的插件以独立的Tab页展现出来。

    Tab控件还有一个重要的名为Position的属性,他决定了我们的Tab在哪个地方展现,PositionType的默认值为Default,这时,如果ControlIdType为Office,我们的插件会在加载项中显示,如果为Custom,插件会显示在Office里面的最后一个加载项后面,如果有多个加载项,则按顺序往后面排列。PositionType属性还有BeforeOfficeId和AfterOfficeId两值,设置这两个属性时,需要指定OfficeId,Office内置的菜单的ID值,您可以到MSDN上下载。指定好OfficeId后,我们的Tab也会在该内置Tab页里面,前面或者后面显示。一般的,我们保持这个属性为空即可。让我们的插件在Office系统Tab页之后显示。

    2.2 Group控件

    Group控件的作用是将我们的功能进行分组。回到我们之前的规划,我们的歪歪插件有财经,地图,天气,关于这几大功能。所以我们需要在界面上放置4个Group控件,并对其进行命名。

    groupcontrol

    2.3 Menu控件和SpliterButton控件

    在分好类之后,我们需要对每个分类的细小功能进行设计,这里面我们需要放置各种控件,首先我们可能会接触到的就是Menu控件,里面可以包含Button,SplitButton等。我们规划的财经项主要包括,实时行情,历史行情和导入功能。实时行情和历史行情包括从Sina或者Yahoo财经API接口中获取所有股票的实时和历史行情数据,导入功能即是从本地或者网络上导入数据。所以在页面上添加三个Menu控件。

    Menu Property

    Menu控件几个比较重要的属性,一个是ControlSize,它确定了控件的大小,一般地,如果功能较重要,或者是有比较明显的分类用途,使用RibbonControlSizeLarge。然后需要设置菜单的图标,可以指定自定义的图片,也可以使用默认的Office内置的菜单的图片,如果要使用内置的图片,需要设置OfficeImageId,具体内置Id及图片可以查看该网址。一般地,我们会为我们的菜单设计图标,您只需要指定其Image属性即可。

    SplitButton控件和Menu控件类似,它可以包含Button,Seperator控件,不同的是,SplitButton控件本身自己可以响应Click事件,通常在Menu中如果需要将该菜单项中常用的功能设置为默认的,那么可以使用SplitButton控件,将最常用的功能设置到该控件的Click事件上来。

    2.4 Button,Seperator控件

    Button控件是最基础的响应单击事件的UI控件。点开Menu的下拉图标,然后向里面添加Button按钮即可,可以设置按钮的Image属性。在设计按钮的时候,可能我们需要对其进行分组,这时候,使用Seperator控件是一种比较好的选择,直接在ToolBox中拖动一个Seperator控件到界面上想分割的地方即可。默认情况下Seperator控件是一条竖线,但是当设置Seperator控件的Title属性时,他可以以文本的形式来进行分割,这和其他系统中的Seperator控件不一样。

    VSTO Spliter

    同理,按照规划,我们将所有的菜单设计好,并注册其Click事件。这里只讲解了这几个基本的菜单项控件,更多的控件您可能以自己往界面上拖拽试试看,利用这些内置的控件,您可以设计出和Office内置的Ribbon菜单媲美的自定义菜单界面来。

    YY插件菜单 

    RibbonXml

    在Office中Ribbon菜单时可以通过RibbonXML进行配置,也就是说,上面的可视化界面设计其实是为我们提供了编辑RibbonXML的设计时支持。其实我们也可以直接创建一个XML文件进行设计,然后在代码中进行加载,同样能够实现这样的功能。在有些情况下,比如我们创建SharedAddin程序时,根本没有设计时支持。所以了解RibbonXML对于创建可兼容多版本Excel菜单系统显得尤为重要。

    要创建RibbonXML最好的做法是对着新建的可视化菜单,然后右键->将功能区导出到XML。然后项目会自动创建Ribbon.xml和Ribbon.cs文件,其中Ribbon.xml是布局文件,Ribbon.cs是事件处理代码。

    VSTO Ribbon2XML

    打开Ribbon.xml可以看到如下代码,可以看到这个UI界面的展现即是使用了该XML文件来进行渲染的,其中在XML中还可以声明一些事件,后面会讲。

    VSTO RibbonXML

    现在,如何在我们的应用程序中加载该RibbonXML并渲染出Ribbon菜单呢?首先,我们将之前的添加的可视化设计的Ribbon菜单YYMenu.cs排除到项目外。然后转到ThisAddIn.cs中,覆写Office.IRibbonExtensibility 接口的CreateRibbonExtensibilityObject方法,实例化自动生成的Ribbon类,然后返回。

    private Ribbon customerRibbon;
    
    protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
    {
        customerRibbon = new Ribbon();
        return customerRibbon;
    }

    运行程序,即可看到如下效果:

    YY插件菜单 No Image

    可以看到图片不见了,这是因为在导出功能区为XML的时候,VS没有帮我们处理图片,所以需要我们自己来添加。在RibbonXML文档中, customUI节点下有loadImage事件,Button有getImage事件和image属性。customUI节点的loadImage方法和子节点的image属性一起使用,image属性作为loadImage方法的参数。

    VSTO RibbonXML GetImage

    loadImage方法如下:

    public Image LoadImage(string imageName)
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        //String[] all =assembly.GetManifestResourceNames();//GetResourceName
        Stream stream = assembly.GetManifestResourceStream("YYAddIn.Resources." + imageName);
        return Image.FromStream(stream);
    }

    其中,imageName即为Button控件的image属性要设置的图片名称。资源文件图片,要设置为嵌入的资源。所有的按钮的单击事件,我们使用GeneralButton_Click事件来处理。运行程序,我们又看到了之前采用设计器时编辑的菜单时的界面了。

    二 Excel 2003下面的菜单系统

    创建工程

    由于Excel2003及以下版本不支持Ribbon菜单,所以以上的程序在03版本下并不能运行。但是,如果开发企业级应用的话,仍不能忽视广大使用 Office 2003 版本的客户,所以您还需要创建2003下面的菜单系统。下面就介绍如何在Excel2003系统中创建Excel菜单及工具条。

    要创建在Excel 03下的插件,我们可以创建Shared Add-in程序,如下图,首先新建一个名为YYSharedAddin的Shared Add-in项目:

    SharedAddin

    然后下一步下一步, 设置编程语言,我们这里选择C#

    SharedAddinStep2 

    下一步,设置插件的目标应用程序,可以看到,我们的Shared Add-in程序可以为多个目标应用程序编写同一个插件,这在前一篇文章中介绍SharedAddin和VSTO的差别时已经介绍过了。因为我们主要是编写Excel插件,所以仅勾选Excel即可。

    SharedAddinStep3

    下一步,设置Addin的展现名称和在编程时的名称

    SharedAddinStep4

    创建完成之后,我们可以看到工程项目非常简单,只有一个Connect.cs文件。打开该文件,可以看到其中实现了Extensibility.IDTExtensibility2 接口,所有Office应用程序都是用IDTExtensibility2接口与COM加载项进行通信的,该接口提供了一种通用的初始化机制,并具有在Office应用程序的对象模型中传递数据的能力,因此Com加载项可以与Office应用程序通信,该接口中有5个方法,分别是:

    IDTExtensibility2 接口

    Office在对Com加载项进行实例化时,会创建Connect类,注意我们不能用Connect的构造函数创建类的实例,应该在OnConnection方法中进行初始化操作,比如菜单项的加载,自定义函数的加载,变量的初始化等等;类似的,加载项的关闭不能调用析构函数,而要用OnDisconnection方法,在该方法中需要释放非托管的资源,进行资源清理等一系列操作。下图展示了这5个方法的执行顺序。该图引用了MYM]Brooks同学博文中的图片。

    SharedAddin IDTExtensibility接口方法的执行顺序

    由分析得之,我们对菜单及工具栏的初始化,需要放到OnConnection方法中。

    在OnConnection方法中,有一个很重要的参数就是application,我们声明一个类型为

    Microsoft.Office.Interop.Excel.Application的applicationObject对象,然后将这个对象保存起来,以备后面创建菜单以及对Excel进行操作时使用。

    private Application applicationObject;
    
    /// <summary>
    ///      Implements the OnConnection method of the IDTExtensibility2 interface.
    ///      Receives notification that the Add-in is being loaded.
    /// </summary>
    /// <param term='application'>
    ///      Root object of the host application.
    /// </param>
    /// <param term='connectMode'>
    ///      Describes how the Add-in is being loaded.
    /// </param>
    /// <param term='addInInst'>
    ///      Object representing this Add-in.
    /// </param>
    /// <seealso class='IDTExtensibility2' />
    public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
    {
        applicationObject = application as Application;
        addInInstance = addInInst as COMAddIn;
    
        if (applicationObject.Version == "11.0")
        {
            if (menuDesigner == null)
            {
                menuDesigner = new MenuDesigner(applicationObject);
            }
            menuDesigner.AddMenus();
            menuDesigner.AddToolBars();
        }
    }

    在OnConnection方法中,我们首先判断Excel的版本号,版本号可以通过Version对象获取,如果版本号为11,即为2003版本的Excel,我们需要手动的动态创建菜单和工具条。我们新建了一个名为MenuDesigner的用来创建菜单和工具条的类,在其构造函数中传入了applicationObject对象。

    添加菜单

    我们把添加菜单放到了MenuDesigner的AddMenus方法中。Excel中的一个菜单项和子菜单其实都是一个MSOffice.CommandBarPopup对象,菜单项里面的菜单按钮是MSOffice.CommandBarButton对象。首先我们定义菜单项以及菜单按钮,这里只列出部分。

    //YY插件菜单
    MSOffice.CommandBarPopup YYMenu = null;
    //实时行情函数菜单按钮
    MSOffice.CommandBarButton btnQuoteFunctionMenuCommand = null;
    //Sina实时行情函数菜单按钮
    MSOffice.CommandBarButton btnQuoteSinaFunctionMenuCommand = null;
    //Yahoo实时行情函数菜单按钮
    MSOffice.CommandBarButton btnQuoteYahooFunctionMenuCommand = null;

    在创建菜单时,我们要首先创建YYMenu对象,然后再在该对象上添加菜单项。创建YYMenu菜单的方法如下:

    public void AddMenus()
    {
        MSOffice.CommandBar menubar = (MSOffice.CommandBar)application.CommandBars.ActiveMenuBar;
        int controlCount = menubar.Controls.Count;
        string menuCaption = "歪歪插件";
        // Add the menu.
        try
        {
            YYMenu = (MSOffice.CommandBarPopup)
                application.CommandBars.ActiveMenuBar.FindControl(
                MSOffice.MsoControlType.msoControlPopup, System.Type.Missing, menuTag, true, true);
        }
        catch { }
    
        if (YYMenu != null)
        {
            YYMenu.Delete(Type.Missing);
        }
        YYMenu = (MSOffice.CommandBarPopup)menubar.Controls.Add(MSOffice.MsoControlType.msoControlPopup, missing, missing, controlCount, true);
        YYMenu.Tag = menuTag;
        YYMenu.Caption = menuCaption;
        YYMenu.BeginGroup = true;
    
        LoginGroup();
        FinancialGroup();
        MapServiceGroup();
        WeatherReportGroup();
        AboutGroup();
    }

    需要注意的是,在创建菜单之前,我们需要判断之前是否已经存在相同Tag值得菜单,如果存在,需要先将之前创建的菜单项删除,然后再重新创建。否则,在用户在Com加载项中显示或者隐藏菜单项时,会重复创建菜单项。创建完主菜单后,我们可以在主菜单上创建子菜单了。这些方法都写到了最后的几个以Group结尾的方法中。现在以创建财务项菜单的FinancalGroup方法为例讲解。

    private void FinancialGroup()
    {
        //实时行情
        MSOffice.CommandBarPopup realTimeButton = AddPopupButton(YYMenu.Controls, MenuNameEnum.Quote);
        realTimeButton.BeginGroup = true;
        //添加子菜单
        btnQuoteFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, MenuNameEnum.Quote, YYSharedAddin.Properties.Resources.QuoteReal);
        btnQuoteSinaFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, MenuNameEnum.QuoteSina, YYSharedAddin.Properties.Resources.SinaQuote_64);
        btnQuoteSinaFunctionMenuCommand.BeginGroup = true;
        btnQuoteYahooFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, MenuNameEnum.QuoteYahoo, YYSharedAddin.Properties.Resources.Yahoo_Quote);
    
        //历史行情
        MSOffice.CommandBarPopup historyButton = AddPopupButton(YYMenu.Controls, MenuNameEnum.QuoteHistory);
        realTimeButton.BeginGroup = true;
        //添加子菜单
        btnQuoteHistoryFunctionMenuCommand = AddCommandButton(historyButton.Controls, MenuNameEnum.QuoteHistory, YYSharedAddin.Properties.Resources.QuoteHist);
        btnQuoteHistorySinaFunctionMenuCommand = AddCommandButton(historyButton.Controls, MenuNameEnum.QuoteHistorySina, YYSharedAddin.Properties.Resources.SinaQuote_64);
        btnQuoteHistorySinaFunctionMenuCommand.BeginGroup = true;
        btnQuoteHistoryYahooFunctionMenuCommand = AddCommandButton(historyButton.Controls, MenuNameEnum.QuoteHistoryYahoo, YYSharedAddin.Properties.Resources.Yahoo_HistoryQuote);
    
        //导出
        MSOffice.CommandBarPopup importButton = AddPopupButton(YYMenu.Controls, MenuNameEnum.Import);
        importButton.BeginGroup = true;
        btnImportFromLocalMenuCommand = AddCommandButton(importButton.Controls, MenuNameEnum.ImportFromLocal, YYSharedAddin.Properties.Resources.ImportFromDisk);
        btnImportFromWebMenuCommand = AddCommandButton(importButton.Controls, MenuNameEnum.ImportFromWeb, YYSharedAddin.Properties.Resources.ImportFromWeb);
    }

    对于财务大类菜单,其中有三个一级菜单,分别是实时行情,历史行情,导入。创建一级菜单的方法AddPopupButton代码为:

    /// <summary>
    /// 添加子菜单项
    /// </summary>
    /// <param name="controls">该字菜单的父菜单容器</param>
    /// <param name="menu">菜单名称</param>
    /// <returns></returns>
    private MSOffice.CommandBarPopup AddPopupButton(MSOffice.CommandBarControls controls, MenuNameEnum menu)
    {
        String tag = menu.ToString();
        String caption = String.Empty;
        Menus.menus.TryGetValue(tag, out caption);
        MSOffice.CommandBarPopup command = null;
        try
        {
            command = controls[caption] as MSOffice.CommandBarPopup;
        }
        catch { }
        if (command == null)
        {
            command = controls.Add(MSOffice.MsoControlType.msoControlPopup, Type.Missing, Type.Missing, Type.Missing, Type.Missing) as MSOffice.CommandBarPopup;
            command.Caption = caption;
        }
        return command;
    }

    其中第一个参数为最大的根节点菜单YYMenu对象的所有Controls容器。所以创建第一个实时行情一级菜单的方法为:

    //实时行情
    MSOffice.CommandBarPopup realTimeButton = AddPopupButton(YYMenu.Controls, MenuNameEnum.Quote);
    realTimeButton.BeginGroup = true;

    得到realTimeButton这个一级菜单之后,我们将其BeginGroup属性设置为true表示在之前添加一个Seperator控件(一条横线或者竖线)。有了这个realTimeButton一级菜单之后,我们可以在该对象上创建三个二级菜单按钮项。

    //添加子菜单
    btnQuoteFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, MenuNameEnum.Quote, YYSharedAddin.Properties.Resources.QuoteReal);
    btnQuoteSinaFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, MenuNameEnum.QuoteSina, YYSharedAddin.Properties.Resources.SinaQuote_64);
    btnQuoteSinaFunctionMenuCommand.BeginGroup = true;
    btnQuoteYahooFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, MenuNameEnum.QuoteYahoo, YYSharedAddin.Properties.Resources.Yahoo_Quote);

    创建子菜单按钮的方法封装到了AddCommandButton方法中,方法第一个参数为第一级子菜单的所有控件的容器类。

    private MSOffice.CommandBarButton AddCommandButton(MSOffice.CommandBarControls controls, MenuNameEnum menu, System.Drawing.Image icon)
    {
        string tempName = menu.ToString();
        String tag = String.Format("{0}|{1}", menu, DateTime.Now.ToBinary());
        String caption = String.Empty;
        Menus.menus.TryGetValue(tempName, out caption);
        MSOffice.CommandBarButton command = null;
        try
        {
            command = controls[caption] as MSOffice.CommandBarButton;
            command.Tag = tag;
            command.Click += new MSOffice._CommandBarButtonEvents_ClickEventHandler(command_Click);
        }
        catch { }
        if (command == null)
        {
            command = controls.Add(MSOffice.MsoControlType.msoControlButton, Type.Missing, Type.Missing, Type.Missing, Type.Missing) as MSOffice.CommandBarButton;
            command.Style = MSOffice.MsoButtonStyle.msoButtonIconAndCaption;
            command.Caption = caption;
            command.Tag = tag;
            command.Picture = ImageConverterHelper.ImageToPictureDisp(icon);
            command.Click += new MSOffice._CommandBarButtonEvents_ClickEventHandler(command_Click);
        }
        return command;
    }

    在创建菜单按钮时,我们需要先判断当前的菜单中是否有该菜单项,如果有直接使用。否则创建新的对象。这里有几个地方需要注意,首先是CommandBarButton 的Tag属性,该属性应该加上一个唯一标志,比如当前时间,或者GUID,然后带上该菜单的名称等信息。加唯一标志的目的是每一次在创建菜单时保证是唯一的,否则会出现菜单只会响应一次按钮点击事件等奇怪的问题。其次设置按钮的Style为MSOffice.MsoButtonStyle.msoButtonIconAndCaption 既带图片又有文字的时候,Picture属性为想要显示在按钮前面的图片,该对象是一个stdole.IPictureDisp类型的对象,您需要进行一下转换。其次直接设置图片会使得图片中的背景色不会透明,背景色为Office的默认风格颜色,如果要将背景色透明,需要设置Mask属性,Mask属性也是一张图片。该图片为带显示图片的蒙版,即原始图片中需要显示的地方,用黑色表示,那么其余地方就会透明显示,具体使用方式您可以参看这篇文章,这里为了简化,不做处理。

    添加工具条

    工具条其实就是一个大的一级菜单,和创建菜单一样,我们将创建工具条的代码封装到了AddToolBars方法中,该方法代码如下:

    public void AddToolBars()
    {
        try
        {
            YYToolBar = application.CommandBars["YYToolBar"];
        }
        catch { }
    
        if (YYToolBar == null)
        {
            YYToolBar = application.CommandBars.Add("YYToolBar", MSOffice.MsoBarPosition.msoBarTop, false, true);
        }
        LoginGroup_ToolBar();
        FinancialGroup_ToolBar();
        MapServiceGroup_ToolBar();
        WeatherReportGroup_ToolBar();
        AboutGroup_ToolBar();
    
        YYToolBar.Visible = true;
    }

    我们首先需要创建一个大的工具条,和创建菜单类似,在创建工具条之前,我们需要定义好所有的工具条中的按钮,注意,该按钮对象不能和菜单项里面的对象共用,否则会导致事件注册被冲掉的情况。

    //歪歪插件工具条
    MSOffice.CommandBar YYToolBar;
    //实时行情函数工具条
    MSOffice.CommandBarButton btnQuoteFunctionToolBarCommand = null;
    //Sina实时行情工具条
    MSOffice.CommandBarButton btnQuoteSinaFunctionToolBarCommand = null;
    //Yahoo实时行情工具条
    MSOffice.CommandBarButton btnQuoteYahooFunctionToolBarCommand = null;

    创建好YYToolBar对象后,在该对象的基础上创建工具条里面的工具项就和创建字菜单类似了,这里就不再赘述了。

    现在我们来看在Excel2003下面的效果。由于我们创建的SharedAddin程序,我们在调试的时候,需要设置启动程序,Visual Studio 给我们设置的默认启动程序是Visual Studio本身。这里我们将默认程序指定为Excel 2003 的可执行文件,如下图:

    SharedAddin Start external programe

    运行程序,Visual Studio就会启动Excel程序,然后就可以看到我们创建的歪歪菜单和工具条了。

    YYAddinInExcel 2003

    三 全版本兼容

    前面介绍了在2003以上版本的Ribbon菜单创建和在2003版本下面的传统菜单项的创建。一个良好的Excel应用程序应该会根据版本的不同而展现不同的菜单形式。如果您用VSTO创建的话,那么可能在03版本上就不能很好的支持,因为03版本不支持Ribbon菜单。所以要想兼容所有的Excel版本,可以创建Shared Add-in工程。第二部分已经讲解了如何在SharedAddin中创建传统菜单的方法,要兼容03以上版本,我们只需要在SharedAddin中加载第一部分创建好的RibbonXML即可。

    要让SharedAddin能在03以上版本中渲染Ribbon菜单,我们需要让Connect类实现Office.IRibbonExtensibility接口。由于之前生成的Ribbon.cs类已经实现了该接口,所以最简单的方法是:将之前创建好的Ribbon.xml及Ribbon.cs拷贝到SharedAddin工程项目中来。并将Ribbon.xml设置为嵌入的资源。将Ribbon.cs 的命名空间改为和Connect.cs一致的命名空间,然后利用Partial关键字,将Ribbon类名称改为partial Connect类。如下:

    [ComVisible(true)]
    public partial class Connect : Office.IRibbonExtensibility
    {
        private Office.IRibbonUI ribbon;
        #region IRibbonExtensibility 成员
    
        public string GetCustomUI(string ribbonID)
        {
            return GetResourceText("YYSharedAddin.RibbonMenu.Ribbon.xml");
        }
        #endregion
    
        #region 功能区回调
        //在此创建回调方法。有关添加回调方法的详细信息,请在解决方案资源管理器中选择功能区 XML 项,然后按 F1
    
        public void Ribbon_Load(Office.IRibbonUI ribbonUI)
        {
            this.ribbon = ribbonUI;
        }
    
        public Image LoadImage(string imageName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            //String[] all =assembly.GetManifestResourceNames();//GetResourceName
            Stream stream = assembly.GetManifestResourceStream("YYSharedAddin.Resources." + imageName);
            return Image.FromStream(stream);
        }
    
        public void GeneralButton_Click(Office.IRibbonControl control)
        {
            try
            {
                MessageBox.Show("you clicked the button id is " + control.Id);
            }
            catch (Exception ex)
            {
    
            }
        }
        #endregion
    
        #region 帮助器
    
        private static string GetResourceText(string resourceName)
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            string[] resourceNames = asm.GetManifestResourceNames();
            for (int i = 0; i < resourceNames.Length; ++i)
            {
                if (string.Compare(resourceName, resourceNames[i], StringComparison.OrdinalIgnoreCase) == 0)
                {
                    using (StreamReader resourceReader = new StreamReader(asm.GetManifestResourceStream(resourceNames[i])))
                    {
                        if (resourceReader != null)
                        {
                            return resourceReader.ReadToEnd();
                        }
                    }
                }
            }
            return null;
        }
    
        #endregion
    }

    我们需要注意的是,要设置好正确的资源名称,您可以通过GetManifestResourceNames 来查看该程序集中的所有的资源名称。

    现在,将启动项目设置为2007 或者2010版本的Excel,现在菜单又变成Ribbon风格的了:

    YY插件菜单

    将启动项目设置为2003版本的Excel,菜单就变成传统风格的了。

    YYAddinInExcel 2003

    四 结语

    本文介绍了Excel中的菜单系统。首先介绍了使用Visual Studio设计时支持的Ribbon菜单的创建,通过拖拉控件及设置属性,可以创建出和Office内置菜单相媲美的自定义菜单。然后介绍了Ribbon菜单的基础Ribbon XML文件,随后讲解了如何在VSTO中手动加载Ribbon菜单。然而Ribbon菜单仅在2003以上版本的Excel中支持。为了解决Excel 2003下菜单创建的问题,本文展示了如何创建Excel Shared Add-in程序,并演示了如何创建传统的菜单项和工具栏。最后为了兼容所有的Excel版本,在SharedAddin中展示了如何加载Ribbon XML,使得我们的Excel插件对于不同的Excel版本,能够展现出不同风格的菜单项。

    现在我们的插件架子已经搭好了,下文我会讲解Excel的对象模型,介绍Excel中的几个核心对象,如WorkBook,WorkSheet,Range对象等,这些对象无论是您进行何种类型的Excel开发,都会遇到,这些对象也是您进行Excel开发的重要基础,敬请期待。

    本文代码点击此处下载,希望本文对您了解Excel菜单系统有所帮助。

    作者: yangecnuyangecnu's Blog on 博客园) 
    出处:http://www.cnblogs.com/yangecnu/ 
    作品yangecnu 创作,采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请 给我留言
     
  • 相关阅读:
    Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?
    "=="和equals方法究竟有什么区别?
    Tomcat发生java.lang.OutOfMemoryError: PermGen space的解决方案
    HashMap原理
    springMVC
    SqlServer和Oracle修改表结构语句
    quartz定时任务时间表达式说明
    Linq to Entity 时间差作为筛选条件产生的问题
    密码学基础
    C#如何使用异步编程【BeginInvoke/EndInvoke】
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3229308.html
Copyright © 2011-2022 走看看