在開始Excel開發之前,需要把架子搭起來。最直接的那就是Excel裡面的菜單了,他向用戶直觀的展現了我們的插件具有哪些功能。菜單出來之後我們就可以實現裡面的事件和功能了。Excel菜單有兩種形式,一種是Excel 2003及之前的傳統菜單樣式,一種是Excel 2007及之後的Ribbon菜單。本文首先講解Excel 2007中菜單的創建,包括使用Visual Studio可視化設計菜單,菜單的RibbonXml配置,然後講解如何在Excel 2003中創建自定義菜單。最後演示如何使用SharedAddin技術將兩者結合起來,即在2003版本中顯示原始的菜單樣式,在以2003上版本中動態加載Ribbon菜單,從而達到版本的兼容。
要演示菜單的創建,我們首先創建一個VSTO程序,如圖在VS中創建一個Excel外接程序:
2.1 Tab控件
在Office中Ribbon菜單時可以通過RibbonXML進行配置,也就是說,上面的可視化界面設計其實是為我們提供了編輯RibbonXML的設計時支持。其實我們也可以直接創建一個XML文件進行設計,然後在代碼中進行加載,同樣能夠實現這樣的功能。在有些情況下,比如我們創建SharedAddin程序時,根本沒有設計時支持。所以了解RibbonXML對於創建可兼容多版本Excel菜單系統顯得尤為重要。 要創建RibbonXML最好的做法是對著新建的可視化菜單,然後右鍵->將功能區導出到XML。然後項目會自動創建Ribbon.xml和Ribbon.cs文件,其中Ribbon.xml是布局文件,Ribbon.cs是事件處理代碼。
運行程序,即可看到如下效果:
其中,imageName即為Button控件的image屬性要設置的圖片名稱。資源文件圖片,要設置為嵌入的資源。所有的按鈕的單擊事件,我們使用GeneralButton_Click事件來處理。運行程序,我們又看到了之前采用設計器時編輯的菜單時的界面了。 由於Excel2003及以下版本不支持Ribbon菜單,所以以上的程序在03版本下並不能運行。但是,如果開發企業級應用的話,仍不能忽視廣大使用 Office 2003 版本的客戶,所以您還需要創建2003下面的菜單系統。下面就介紹如何在Excel2003系統中創建Excel菜單及工具條。 要創建在Excel 03下的插件,我們可以創建Shared Add-in程序,如下圖,首先新建一個名為YYSharedAddin的Shared Add-in項目:
在OnConnection方法中,我們首先判斷Excel的版本號,版本號可以通過Version對象獲取,如果版本號為11,即為2003版本的Excel,我們需要手動的動態創建菜單和工具條。我們新建了一個名為MenuDesigner的用來創建菜單和工具條的類,在其構造函數中傳入了applicationObject對象。 添加菜單 我們把添加菜單放到了MenuDesigner的AddMenus方法中。Excel中的一個菜單項和子菜單其實都是一個MSOffice.CommandBarPopup對象,菜單項裡面的菜單按鈕是MSOffice.CommandBarButton對象。首先我們定義菜單項以及菜單按鈕,這裡只列出部分。 在創建菜單時,我們要首先創建YYMenu對象,然後再在該對象上添加菜單項。創建YYMenu菜單的方法如下: 需要注意的是,在創建菜單之前,我們需要判斷之前是否已經存在相同Tag值得菜單,如果存在,需要先將之前創建的菜單項刪除,然後再重新創建。否則,在用戶在Com加載項中顯示或者隱藏菜單項時,會重復創建菜單項。創建完主菜單後,我們可以在主菜單上創建子菜單了。這些方法都寫到了最後的幾個以Group結尾的方法中。現在以創建財務項菜單的FinancalGroup方法為例講解。 對於財務大類菜單,其中有三個一級菜單,分別是實時行情,歷史行情,導入。創建一級菜單的方法AddPopupButton代碼為: 其中第一個參數為最大的根節點菜單YYMenu對象的所有Controls容器。所以創建第一個實時行情一級菜單的方法為: 得到realTimeButton這個一級菜單之後,我們將其BeginGroup屬性設置為true表示在之前添加一個Seperator控件(一條橫線或者豎線)。有了這個realTimeButton一級菜單之後,我們可以在該對象上創建三個二級菜單按鈕項。 創建子菜單按鈕的方法封裝到了AddCommandButton方法中,方法第一個參數為第一級子菜單的所有控件的容器類。 在創建菜單按鈕時,我們需要先判斷當前的菜單中是否有該菜單項,如果有直接使用。否則創建新的對象。這裡有幾個地方需要注意,首先是CommandBarButton 的Tag屬性,該屬性應該加上一個唯一標志,比如當前時間,或者GUID,然後帶上該菜單的名稱等信息。加唯一標志的目的是每一次在創建菜單時保證是唯一的,否則會出現菜單只會響應一次按鈕點擊事件等奇怪的問題。其次設置按鈕的Style為MSOffice.MsoButtonStyle.msoButtonIconAndCaption 既帶圖片又有文字的時候,Picture屬性為想要顯示在按鈕前面的圖片,該對象是一個stdole.IPictureDisp類型的對象,您需要進行一下轉換。其次直接設置圖片會使得圖片中的背景色不會透明,背景色為Office的默認風格顏色,如果要將背景色透明,需要設置Mask屬性,Mask屬性也是一張圖片。該圖片為帶顯示圖片的蒙版,即原始圖片中需要顯示的地方,用黑色表示,那麼其余地方就會透明顯示,具體使用方式您可以參看這篇文章,這裡為了簡化,不做處理。 添加工具條 工具條其實就是一個大的一級菜單,和創建菜單一樣,我們將創建工具條的代碼封裝到了AddToolBars方法中,該方法代碼如下: 我們首先需要創建一個大的工具條,和創建菜單類似,在創建工具條之前,我們需要定義好所有的工具條中的按鈕,注意,該按鈕對象不能和菜單項裡面的對象共用,否則會導致事件注冊被沖掉的情況。 創建好YYToolBar對象後,在該對象的基礎上創建工具條裡面的工具項就和創建字菜單類似了,這裡就不再贅述了。 現在我們來看在Excel2003下面的效果。由於我們創建的SharedAddin程序,我們在調試的時候,需要設置啟動程序,Visual Studio 給我們設置的默認啟動程序是Visual Studio本身。這裡我們將默認程序指定為Excel 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類。如下: 我們需要注意的是,要設置好正確的資源名稱,您可以通過GetManifestResourceNames 來查看該程序集中的所有的資源名稱。 現在,將啟動項目設置為2007 或者2010版本的Excel,現在菜單又變成Ribbon風格的了:
本文介紹了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菜單系統有所幫助。MSOffice.YYMenu = ;
MSOffice.btnQuoteFunctionMenuCommand = ;
MSOffice.btnQuoteSinaFunctionMenuCommand = ;
MSOffice.btnQuoteYahooFunctionMenuCommand = ;
AddMenus()
{
MSOffice.menubar = (MSOffice.)application.CommandBars.ActiveMenuBar;
controlCount = menubar.Controls.Count;
menuCaption = ;
{
YYMenu = (MSOffice.)
application.CommandBars.ActiveMenuBar.FindControl(
MSOffice..msoControlPopup, System..Missing, menuTag, , );
}
{ }
(YYMenu != )
{
YYMenu.Delete(.Missing);
}
YYMenu = (MSOffice.)menubar.Controls.Add(MSOffice..msoControlPopup, missing, missing, controlCount, );
YYMenu.Tag = menuTag;
YYMenu.Caption = menuCaption;
YYMenu.BeginGroup = ;
LoginGroup();
FinancialGroup();
MapServiceGroup();
WeatherReportGroup();
AboutGroup();
}
FinancialGroup()
{
MSOffice.realTimeButton = AddPopupButton(YYMenu.Controls, .Quote);
realTimeButton.BeginGroup = ;
btnQuoteFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, .Quote, YYSharedAddin.Properties..QuoteReal);
btnQuoteSinaFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, .QuoteSina, YYSharedAddin.Properties..SinaQuote_64);
btnQuoteSinaFunctionMenuCommand.BeginGroup = ;
btnQuoteYahooFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, .QuoteYahoo, YYSharedAddin.Properties..Yahoo_Quote);
MSOffice.historyButton = AddPopupButton(YYMenu.Controls, .QuoteHistory);
realTimeButton.BeginGroup = ;
btnQuoteHistoryFunctionMenuCommand = AddCommandButton(historyButton.Controls, .QuoteHistory, YYSharedAddin.Properties..QuoteHist);
btnQuoteHistorySinaFunctionMenuCommand = AddCommandButton(historyButton.Controls, .QuoteHistorySina, YYSharedAddin.Properties..SinaQuote_64);
btnQuoteHistorySinaFunctionMenuCommand.BeginGroup = ;
btnQuoteHistoryYahooFunctionMenuCommand = AddCommandButton(historyButton.Controls, .QuoteHistoryYahoo, YYSharedAddin.Properties..Yahoo_HistoryQuote);
MSOffice.importButton = AddPopupButton(YYMenu.Controls, .Import);
importButton.BeginGroup = ;
btnImportFromLocalMenuCommand = AddCommandButton(importButton.Controls, .ImportFromLocal, YYSharedAddin.Properties..ImportFromDisk);
btnImportFromWebMenuCommand = AddCommandButton(importButton.Controls, .ImportFromWeb, YYSharedAddin.Properties..ImportFromWeb);
}
MSOffice.AddPopupButton(MSOffice.controls, menu)
{
tag = menu.ToString();
caption = .Empty;
.menus.TryGetValue(tag, caption);
MSOffice.command = ;
{
command = controls[caption] MSOffice.;
}
{ }
(command == )
{
command = controls.Add(MSOffice..msoControlPopup, .Missing, .Missing, .Missing, .Missing) MSOffice.;
command.Caption = caption;
}
command;
}
MSOffice.realTimeButton = AddPopupButton(YYMenu.Controls, .Quote);
realTimeButton.BeginGroup = ;
btnQuoteFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, .Quote, YYSharedAddin.Properties..QuoteReal);
btnQuoteSinaFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, .QuoteSina, YYSharedAddin.Properties..SinaQuote_64);
btnQuoteSinaFunctionMenuCommand.BeginGroup = ;
btnQuoteYahooFunctionMenuCommand = AddCommandButton(realTimeButton.Controls, .QuoteYahoo, YYSharedAddin.Properties..Yahoo_Quote);
MSOffice.AddCommandButton(MSOffice.controls, menu, System.Drawing.icon)
{
tempName = menu.ToString();
tag = .Format(, menu, .Now.ToBinary());
caption = .Empty;
.menus.TryGetValue(tempName, caption);
MSOffice.command = ;
{
command = controls[caption] MSOffice.;
command.Tag = tag;
command.Click += MSOffice.(command_Click);
}
{ }
(command == )
{
command = controls.Add(MSOffice..msoControlButton, .Missing, .Missing, .Missing, .Missing) MSOffice.;
command.Style = MSOffice..msoButtonIconAndCaption;
command.Caption = caption;
command.Tag = tag;
command.Picture = .ImageToPictureDisp(icon);
command.Click += MSOffice.(command_Click);
}
command;
}
AddToolBars()
{
{
YYToolBar = application.CommandBars[];
}
{ }
(YYToolBar == )
{
YYToolBar = application.CommandBars.Add(, MSOffice..msoBarTop, , );
}
LoginGroup_ToolBar();
FinancialGroup_ToolBar();
MapServiceGroup_ToolBar();
WeatherReportGroup_ToolBar();
AboutGroup_ToolBar();
YYToolBar.Visible = ;
}
MSOffice.YYToolBar;
MSOffice.btnQuoteFunctionToolBarCommand = ;
MSOffice.btnQuoteSinaFunctionToolBarCommand = ;
MSOffice.btnQuoteYahooFunctionToolBarCommand = ;
[()]
: Office.{
Office.ribbon;
IRibbonExtensibility 成員
GetCustomUI(ribbonID)
{
GetResourceText();
}
功能區回調
Ribbon_Load(Office.ribbonUI)
{
.ribbon = ribbonUI;
}
LoadImage(imageName)
{
assembly = .GetExecutingAssembly();
stream = assembly.GetManifestResourceStream(+ imageName);
.FromStream(stream);
}
GeneralButton_Click(Office.control)
{
{
.Show(+ control.Id);
}
(ex)
{
}
}
幫助器
GetResourceText(resourceName)
{
asm = .GetExecutingAssembly();
[] resourceNames = asm.GetManifestResourceNames();
(i = 0; i < resourceNames.Length; ++i)
{
(.Compare(resourceName, resourceNames[i], .OrdinalIgnoreCase) == 0)
{
(resourceReader = (asm.GetManifestResourceStream(resourceNames[i])))
{
(resourceReader != )
{
resourceReader.ReadToEnd();
}
}
}
}
;
}
}