要实现ribbon菜单只需实现IRibbonExtensibility接口,此接口在程序库Microsoft Office 12.0 Object Library<2.4> 中找到(名称的版本号会随Office安装版本的不同而略有区别)。(方法跟上一节添加接口的流程一样这里就不在啰嗦了)。
1.实现接口。将继承声明中的&LIBID_Office改为&__uuidof(__Office),在stdafx.h中对命名空间和方法重命名以避免冲突。如下所示:
#import "C:\Program Files (x86)\Common Files\DESIGNER\MSADDNDR.DLL" auto_rename auto_search raw_interfaces_only rename_namespace("AddinDesign") #import "C:\Program Files (x86)\Common Files\Microsoft Shared\OFFICE12\MSO.DLL" auto_rename auto_search raw_interfaces_only rename_namespace("Office") rename("RGB","MsoRGB") rename("SearchPath","MsoSearchPath") using namespace AddinDesign; using namespace Office;
这里跟MSDN上一样 只是改下office的路径。
2、添加ribbon描述xml。可用MSDN中的例子修改。
因为C++开发没有设计的可视化界面,这里有一个方法是创建一个C# excel插件项目,然后对ribbon菜单按照设计设计,完成后选择导出xml,将文件拷贝到工程中,稍加修改即可。选择资源视图-》添加-》资源-》导入-》选择该xml文件路径.
文件内容大致为:
<?xml version="1.0" encoding="utf-16BE"?>
<customUI onLoad="RibbonLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui" loadImage="GetImage">
<ribbon>
<tabs>
<!--getImage="ribbon_GetImage"-->
<tab id="YYtab" label="新华财经">
<group id="grpFinancial" label="财经">
<splitButton id="splitButton1" size="large" >
<button id="btnQuote" getEnabled="EnableRibbonUi" label="实时行情 " onAction="GeneralButton_Click" image="205"/>
<menu id="splitButton1__mnu" getEnabled="EnableRibbonUi">
<menuSeparator id="separator1" title="行情函数" />
<button id="btnQuoteFunction" onAction="GeneralButton_Click" keytip="R" label="实时行情" image="205" />
<menuSeparator id="separator2" title="实时行情" />
<button id="btnQuoteYahoo" onAction="GeneralButton_Click" description="获取实时行情" label="Yahoo实时" image="205" />
<button id="btnQuoteSina" onAction="GeneralButton_Click" label="Sina实时" image="SinaQuote_64.png" />
</menu>
</splitButton>
<menu id="MenuHisQuote" label="历史行情 " size="large" image="205" itemSize="large" getEnabled="EnableRibbonUi">
<menuSeparator id="separator3" title="行情函数" />
<button id="btnQuoteHistFunction" onAction="GeneralButton_Click" label="历史行情"/>
<menuSeparator id="separator4" title="历史行情" />
<button id="btnQuoteHisYahoo" onAction="GeneralButton_Click" label="Yahoo历史" />
<button id="btnQuoteHisSina" onAction="GeneralButton_Click" label="Sina历史" />
</menu>
<menu id="MenuImport" label="导入 " size="large" description="Google地图">
<button id="btnImportFromLocal" onAction="GeneralButton_Click" label="本地导入"/>
<button id="btnImportFromWeb" onAction="GeneralButton_Click" label="网络导入"/>
</menu>
</group>
<group id="grpMap" label="地图">
<button id="btnGoogleMap" onAction="HistoryButtonClicked" label="Google地图 " size="large" description="Google地图"/>
<button id="btnGeoLocation" onAction="GeneralButton_Click" label="地理定位 " size="large" />
</group>
<group id="grpWeather" label="天气">
<button id="btnWeatherFunction" onAction="GeneralButton_Click" label="天气函数" size="large" />
<button id="btnWeatherReport" onAction="GeneralButton_Click" label="天气报表" size="large" />
</group>
<group id="grpAbout" label="关于">
<button id="btnLogin" onAction="GeneralButton_Click" label="登录" size="large" />
<button id="btnHelp" onAction="GeneralButton_Click" label="帮助" />
<button id="btnAbout" onAction="GeneralButton_Click" label="关于" />
<button id="btnConfig" onAction="GeneralButton_Click" label="设置"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
这里对这个文件稍微做加解释,详细的可到网上查询。exce插件的所有接口都是以回调的方式给开发者,所有这里里面的RibbonLoad, GetImage HistoryButtonClicked getEnabled都是回调的接口,是需要在项目中实现这些接口的。比如STDMETHOD(RibbonLoad)(IDispatch* ppiDispatch);参数ppiDispatch实际是整个ribbon菜单的对象。excel插件会通过GetImage 函数来加载菜单设置的图片。这里面还有很多这样的回调函数,可根据需要实现相应的接口。本例子实现了部分,可仿照实现即可。有疑问可参考:
{
if(!RibbonXml)
return E_POINTER;
*RibbonXml = GetXMLResource(IDR_XML1);
return S_OK;
}
HRESULT CConnect::HrGetResource(int nId, LPCTSTR lpType, LPVOID* ppvResourceData, DWORD* pdwSizeInBytes)
{
HMODULE hModule = _AtlBaseModule.GetModuleInstance();
if (!hModule)
return E_UNEXPECTED;
HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(nId), lpType);
if (!hRsrc)
return HRESULT_FROM_WIN32(GetLastError());
HGLOBAL hGlobal = LoadResource(hModule, hRsrc);
if (!hGlobal)
return HRESULT_FROM_WIN32(GetLastError());
*pdwSizeInBytes = SizeofResource(hModule, hRsrc);
*ppvResourceData = LockResource(hGlobal);
return S_OK;
}
BSTR CConnect::GetXMLResource(int nId)
{
LPVOID pResourceData = NULL;
DWORD dwSizeInBytes = 0;
HRESULT hr = HrGetResource(nId, _T("XML"),
&pResourceData, &dwSizeInBytes);
if (FAILED(hr))
return NULL;
CComBSTR cbstr(dwSizeInBytes, reinterpret_cast<LPCSTR>(pResourceData));
return cbstr.Detach();
}
BEGIN_COM_MAP(CConnect)
COM_INTERFACE_ENTRY(IConnect)
COM_INTERFACE_ENTRY2(IDispatch, IConnect)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
COM_INTERFACE_ENTRY(IRibbonExtensibility)
END_COM_MAP()
interface IConnect : IDispatch{ HRESULT HistoryButtonClicked([in] IDispatch* ribbon); HRESULT GetImage([in] BSTR *pbstrImageId, [out, retval] IPictureDisp ** ppdispImage); };
STDMETHOD(HistoryButtonClicked)(IDispatch* ribbon);
STDMETHOD(GetImage)(BSTR *pbstrImageId,IPictureDisp ** ppdispImage);
{
int pngId(0);
try
{
pngId = lexical_cast<int>(*pbstrImageId);
}
catch(...)
{
return E_UNEXPECTED;
}
using namespace Gdiplus;
LPVOID pResourceData = NULL;
DWORD len = 0;
HRESULT hr = HrGetResource(pngId,_T("PNG"), &pResourceData, &len);
BYTE* lpRsrc = reinterpret_cast<BYTE*>(pResourceData);
if (!lpRsrc)
{
return E_UNEXPECTED;
}
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem,lpRsrc,len);
GlobalUnlock(m_hMem);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
PICTDESC pic;
memset(&pic, 0, sizeof pic);
Bitmap *png = Bitmap::FromStream(pstm);
HBITMAP hMap = NULL;
png->GetHBITMAP(Color(),&hMap);
pic.picType = PICTYPE_BITMAP;
pic.bmp.hbitmap = hMap;
OleCreatePictureIndirect(&pic,IID_IPictureDisp,true,(LPVOID*)ppdispImage);
return S_OK;
}
因为资源中图片的ID是数字,而xml中image的属性值是字符串,所有将图片的ID以字符串的方式放在image中,在回调中再转为数字以查找资源,方法比较笨,等以后发现更好的方法再做修改。
注:1.我们添加的菜单如果交给excel处理会出现图标跟文字排版不美观的问题,解决这个问题的方法师换行如:就是在文字后面添加 
2.如果出现乱码请修改文件的编码
经过上述步骤 即可完成自定义ribbon菜单的全部过程。