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外接程序:

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

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

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

基本控件

2.1 Tab控件

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

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

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

一般的,我们会采用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控件,并对其进行命名。

2.3 Menu控件和SpliterButton控件

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

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控件不一样。

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

RibbonXml

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

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

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

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

private Ribbon customerRibbon;

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

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

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

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项目:

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

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

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

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

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

由分析得之,我们对菜单及工具栏的初始化,需要放到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 的可执行文件,如下图:

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

三 全版本兼容

前面介绍了在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风格的了:

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

四 结语

本文介绍了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 中国大陆许可协议进行许可。 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请 给我留言
 

Excel 菜单系统的更多相关文章

  1. 浅谈Excel开发:二 Excel 菜单系统

    在开始Excel开发之前,需要把架子搭起来.最直接的那就是Excel里面的菜单了,他向用户直观的展现了我们的插件具有哪些功能.菜单出来之后我们就可以实现里面的事件和功能了.Excel菜单有两种形式,一 ...

  2. 浅谈Excel开发:二 Excel 菜单系统(转)

    编辑器加载中...http://www.cnblogs.com/yangecnu/p/Excel-Menu-System-Introduction.html 在开始Excel开发之前,需要把架子搭起来 ...

  3. 在WordPress后台菜单系统中添加Home链接

    在wordpress后台如果想打开前台的话,要想先把鼠标移动到左上角菜单,然后在下拉菜单中点击“查看站点”,很是麻烦,能不能在 WordPress 后台菜单系统中添加 Home 链接呢? 将下面代码复 ...

  4. Shell实现多级菜单系统安装维护脚本实例分享

    Shell实现多级菜单系统安装维护脚本实例分享 这篇文章主要介绍了Shell实现多级菜单系统安装维护脚本实例分享,本文脚本用多级菜单实现管理WEB服务器.Mysql服务器.Nginx服器等,需要的朋友 ...

  5. Joomla - 菜单系统 (从创建到前端页面显示的过程)

    在 Joomla 中,菜单是最常用且重要的功能之一,一般用于承载页面内容和各内容间的切换.导航等,演绎着非常重要的角色: 一.新建菜单 进入后台,点击顶栏菜单 -> 菜单管理 -> 点击新 ...

  6. 手把手带你实现基于 Vite+Vue3 的在线Excel表格系统

    今天,葡萄带你了解如何基于Vite+Vue3实现一套纯前端在线表格系统. 在正式开始项目介绍之前,首先咱们首先来介绍一下Vite和Vue3. Vue3 2020年09月18日Vue.js 3.0发布, ...

  7. 第三次作业——将排课Excel导入系统

    031302322 031302316 将教师排课表导入系统 使用powerdesigner设计数据库表格 设计概念模型 打开new -> Conceptual Data Model创建概念模型 ...

  8. 多级菜单系统安装维护shell脚本实现企业级案例

    演示效果: 1.一级菜单 2.二级菜单 3.执行操作 脚本参考: #!/bin/bash #author lic(oldboy linux student) #date 1304 DISK_NO=&q ...

  9. Office EXCEL 不用VB,你也可以制作自己的Excel菜单!

    还记得这个讨厌的VB吗?为了做一个COM插件,生成一个DLL,麻烦一大堆.其实我们想要的仅仅是把自己写的宏封装一下,更好的调用而已. 打开工具,自定义,在命令菜单中选择新菜单,然后拖放右侧的新菜单到顶 ...

随机推荐

  1. .net mvc mssql easyui treegrid 及时 编辑 ,支持拖拽

    这里提到了,1个问题,怎么扩展 Easyui 参见: http://blog.csdn.net/chenkai6529/article/details/17528833 @{ ViewBag.Titl ...

  2. Kafka的常用管理命令

    1. 查看kafka都有那些topic a. list/usr/hdp/current/kafka-broker/bin/kafka-topics.sh --list --zookeeper test ...

  3. Shell的学习就从重装系统开始吧

    小标题:与Fedora的爱恨情仇 干巴巴的shell学习实在枯燥,看来学习姿势还是要从实践入手 起因:传说中的不作死就不会死,昨晚偶遇一本PDF,讲glade编辑界面的,以下就被吸引了,跟着讲解搞出个 ...

  4. C# FileSystemWatcher 监视磁盘文件

    C# FileSystemWatcher 监视磁盘文件变更 简化需求:有一个简化了的需求是这样的:有一个拍照程序在运行,一旦抓拍之后则将图片文件存储至某目录,然后图片要上传至远程服务器并update数 ...

  5. OCP-1Z0-051-题目解析-第1题

    1. View the Exhibit and examine the structure of the SALES, CUSTOMERS, PRODUCTS, and TIMES tables. T ...

  6. PhpStorm创建Drupal模块项目开发教程

    在PhpStorm开发工具中,创建Drupal开发项目有两种方式:整合Drupal到现有的项目中和直接创建一个新的Drupal模块. 接下来将展示这两种方式的具体操作! 整合Drupal到现有的项目 ...

  7. 在OpenWrt上编写自己的硬件操作程序

    上一篇文章中有写到如何使用OPENWRT的SDK,这里继续,写怎么在上面开发自己的应用程序. 我欲在OpenWrt上编写一个软件,它能够去读取某个AD芯片的多通道采样值. 在看这篇文章之前请看这官方的 ...

  8. ubuntu下的apache+php+mysql的安装

    平时我都时在windows下搭配apache+php+mysql环境的,只不过后来听别人说在linux下搭配apache+php+mysql更受欢迎,而且一般公司也是用这样的搭配,所以今天在试着在ub ...

  9. 【【分享】深入浅出WPF全系列教程及源码 】

    因为原书作者的一再要求,在此声明,本书中的部分内容引用了原书名为<深入浅出WPF>的部分内容,假设博文不能满足你现有的学习须要,能够购买正版图书! 本人10月份提出离职,可是交接非常慢,预 ...

  10. Redmine(Ruby)配置经验

    Redmine(Ruby)配置经验记录在配置Redmine邮件同步过程中遇到的各种问题与解决方法 1. 如何安装Redminehttp://www.redmine.org/projects/redmi ...