Visual Studio 2010響應(yīng)Ribbon控件消息
Visual Studio 2010與Windows 7的完美配合,在本文中體現(xiàn)在Visual Studio 2010響應(yīng)Ribbon控件消息上。UI美工們已經(jīng)幫我們完成了設(shè)計(jì)工作,下面現(xiàn)在,該輪到程序員上場了!51CTO編輯推薦《Visual Studio 2010應(yīng)用與開發(fā)詳解》
在Ribbon界面編輯完成的基礎(chǔ)上,我們來看看如何利用Windows Scenic Ribbon API處理各種控件消息,讓Ribbon界面真正地投入使用。
如何利用XML文件創(chuàng)建Ribbon控件,對控件進(jìn)行排布和設(shè)置控件的縮放策略等等,可以說,這些工作都是UI設(shè)計(jì)師的任務(wù)。對程序員來說,更重要的是對控件的消息進(jìn)行處理實(shí)現(xiàn)其業(yè)務(wù)邏輯。
添加消息處理功能
我們可以創(chuàng)建了一個(gè)派生自IUIApplication的Ribbon界面宿主對象類CApplication,并利用這個(gè)類完成了Ribbon界面的創(chuàng)建,初始化以及與Windows應(yīng)用程序的集成。要對Ribbon控件的消息進(jìn)行處理,我們還是要借助這個(gè)宿主對象類。為了使得CApplication類具有控件消息處理的能力,我們需要修改它的定義,讓它同時(shí)也從IUICommandHandler派生。而IUICommandHandler類,則是Scenic Ribbon API提供給我們的控件消息處理類,只要CApplication從這個(gè)類派生,就具有了相應(yīng)的控件消息的處理能力。
為了對Visual Studio 2010響應(yīng)Ribbon控件消息進(jìn)行處理,我們修改CApplication類的定義如下:
- // 引入Scenic Ribbon API的頭文件
- #include <uiribbon.h>
- // 引入編譯生成的資源頭文件
- #include "ribbonres.h"
- // 調(diào)用Windows API獲得系統(tǒng)時(shí)間
- #include "windows.h"
- #include "stdio.h"
- IUIFramework* g_pFramework = NULL;
- // Ribbon界面宿主程序
- class CApplication
- : public CComObjectRootEx<CComMultiThreadModel>
- , public IUIApplication // 處理Ribbon界面的創(chuàng)建和初始化
- , public IUICommandHandler // 處理Ribbon控件的消息
然后,我們需要利用COM_INTERFACE_ENTRY宏定義COM_MAP,在CApplication類的定義中,添加:
- public:
- BEGIN_COM_MAP(CApplication)
- COM_INTERFACE_ENTRY(IUIApplication)
- COM_INTERFACE_ENTRY(IUICommandHandler)
- END_COM_MAP()
這樣,Visual Studio 2010響應(yīng)Ribbon控件的消息就會發(fā)送到CApplication類,由CApplication類進(jìn)行處理。在整個(gè)Scenic Ribbon API中,Ribbon控件消息的處理流程如下圖所示。除了之前我們介紹的跟Ribbon界面創(chuàng)建和初始化相關(guān)的過程之外,為了完成控件消息,我們首先需要為感興趣的控件進(jìn)行注冊,這些工作都會在OnCreateCommand函數(shù)中完成。完成控件的注冊后,當(dāng)我們需要進(jìn)行消息處理的控件有消息發(fā)生時(shí),IUIFramework就會將這些消息發(fā)送給CApplication,而我們就可以在CApplication類中對具體的消息進(jìn)行處理。Ribbon控件的消息主要分成兩種:一種是來自控件的動(dòng)作消息,比如按鈕控件被點(diǎn)擊,ComboBox的選擇發(fā)生了變化等。這類消息都在Execute函數(shù)中進(jìn)行處理;另外一種是則對控件屬性進(jìn)行更新的消息,這類消息在UpdateProperty函數(shù)中進(jìn)行處理。

注冊感興趣的控件
按照Ribbon界面控件消息的處理流程,我們首先需要在OnCreateCommand函數(shù)中為我們感興趣的控件進(jìn)行注冊,這樣當(dāng)控件有動(dòng)作發(fā)生的時(shí)候,CApplication類才會收到相應(yīng)的控件消息,進(jìn)而可以對其進(jìn)行處理。在CApplication類的OnCreateCommand函數(shù)中,我們完成相應(yīng)控件的注冊:
- STDMETHOD(OnCreateUICommand)(UINT32 nCmdID, __in UI_COMMANDTYPE typeID, __deref_out IUICommandHandler** ppCommandHandler)
- {
- // 對相應(yīng)控件的消息進(jìn)行注冊
- if (nCmdID == cmdMyButton
- || nCmdID == cmdDeleteTable
- || nCmdID == cmdAddTable
- || nCmdID == cmdPrintRelationships)
- {
- return QueryInterface(IID_PPV_ARGS(ppCommandHandler));
- }
- return E_NOTIMPL;
- }
在這段代碼中,我們根據(jù)控件的Symbol選取了幾個(gè)需要進(jìn)行消息處理的控件,當(dāng)這幾個(gè)控件有動(dòng)作發(fā)生時(shí),比如比鼠標(biāo)點(diǎn)擊,或者是進(jìn)行了選擇,IUIFramework會發(fā)送相應(yīng)的消息給宿主對象CApplication,從而讓我們可以有機(jī)會對這些消息進(jìn)行處理。
處理控件消息
對于普通的控件點(diǎn)擊消息或者是選擇消息等動(dòng)作消息,我們需要重寫CApplication類的消息處理函數(shù)Execute函數(shù),在其中對消息進(jìn)行處理:
- // 消息處理函數(shù)
- STDMETHODIMP Execute(UINT nCmdID,
- UI_EXECUTIONVERB verb,
- __in_opt const PROPERTYKEY* key,
- __in_opt const PROPVARIANT* ppropvarValue,
- __in_opt IUISimplePropertySet* pCommandExecutionProperties)
- {
- HRESULT hr = S_OK;
- switch (verb)
- {
- // 只處理感興趣的消息類型
- case UI_EXECUTIONVERB_EXECUTE:
- // 判斷消息來源
- if (nCmdID == cmdMyButton)
- {
- // 執(zhí)行具體的業(yè)務(wù)邏輯
- // 這里我們獲得系統(tǒng)時(shí)間并進(jìn)行輸出
- SYSTEMTIME sys;
- GetLocalTime( &sys );
- wchar_t strInfo[256] = L"";
- wsprintf( strInfo, L"當(dāng)前系統(tǒng)時(shí)間:
- %4d/%02d/%02d %02d:%02d:%02d.%03d 星期%1d\n",
- sys.wYear,sys.wMonth,sys.wDay,
- sys.wHour,sys.wMinute,sys.wSecond,sys.wMilliseconds,
- sys.wDayOfWeek);
- // 顯示消息框
- MessageBox(NULL, strInfo,
- L"當(dāng)前系統(tǒng)時(shí)間”,
- MB_OK);
- }
- break;
- }
- return hr;
- }
現(xiàn)在編譯運(yùn)行這個(gè)解決方案,當(dāng)我們點(diǎn)擊cmdMyButton 所對應(yīng)的“MyButton”這個(gè)按鈕時(shí),就可以得到一個(gè)消息框報(bào)告當(dāng)前的系統(tǒng)時(shí)間。當(dāng)然,我們這里只是對按鈕控件的動(dòng)作進(jìn)行處理,對于其他類型控件的消息處理,都是按照相同的流程進(jìn)行。
圖2 按鈕控件的點(diǎn)擊事件
在運(yùn)行時(shí)對控件屬性進(jìn)行修改
在某些情況下,我們需要在運(yùn)行時(shí)對控件的屬性進(jìn)行修改。例如,在應(yīng)用程序運(yùn)行的某種狀態(tài)下,我們可能需要禁用某些控件,或者是修改控件的標(biāo)簽文本,圖標(biāo)等等。對控件屬性的修改,可以通過直接修改控件屬性達(dá)到,也可以通過調(diào)用InvalidateUICommand函數(shù)刷新控件的屬性,然后在UpdateProperty函數(shù)中進(jìn)行控件屬性更改消息處理,實(shí)現(xiàn)具體的屬性修改。下面我們分別來看看這兩種方式是如何進(jìn)行的。
在這個(gè)例子中,我們處理兩個(gè)按鈕控件的點(diǎn)擊消息,讓他們分別禁用另外的按鈕控件和修改按鈕的標(biāo)簽文本。在Execute函數(shù)中,處理相應(yīng)的按鈕消息,實(shí)現(xiàn)控件屬性的改變:
- STDMETHODIMP Execute(UINT nCmdID,
- UI_EXECUTIONVERB verb,
- __in_opt const PROPERTYKEY* key,
- __in_opt const PROPVARIANT* ppropvarValue,
- __in_opt IUISimplePropertySet* pCommandExecutionProperties)
- {
- HRESULT hr = S_OK;
- switch (verb)
- {
- case UI_EXECUTIONVERB_EXECUTE:
- if (nCmdID == cmdMyButton)
- {
- //
- PROPVARIANT varNew;
- BOOL _fEnabled = FALSE;
- // 初始化屬性值
- hr = UIInitPropertyFromBoolean(UI_PKEY_Enabled,
- _fEnabled, &varNew);
- if (FAILED(hr))
- {
- return hr;
- }
- // 為控件cmdDeleteTable設(shè)置新的屬性值
- hr = g_pFramework->SetUICommandProperty(cmdDeleteTable,
- UI_PKEY_Enabled, varNew);
- if (FAILED(hr))
- {
- return hr;
- }
- }
- else if (nCmdID == cmdAddTable)
- {
- // 發(fā)送屬性更新消息對控件屬性UI_PKEY_Label進(jìn)行更新
- hr = g_pFramework->InvalidateUICommand(
- cmdPrintRelationships,
- UI_INVALIDATIONS_PROPERTY, &UI_PKEY_Label);
- if (FAILED(hr))
- {
- return hr;
- }
- }
- break;
- }
- return hr;
- }
在cmdMyButton按鈕的消息處理中,我們使用SetUICommandProperty就可以直接修改控件的屬性了。在這里,我們通過設(shè)置cmdDeleteTable按鈕控件的UI_PKEY_Enabled屬性為FALSE,達(dá)到了禁用這個(gè)控件的目的。而在cmdAddTable按鈕的消息處理中,我們只是調(diào)用了IUIFramework的InvalidateUICommand函數(shù),這表示它會自動(dòng)調(diào)用CApplication類的UpdateProperty來實(shí)現(xiàn)控件屬性的更新,所以對于第二種方式,我們還需要實(shí)現(xiàn)這個(gè)函數(shù),在其中完成屬性的更新:
- STDMETHODIMP UpdateProperty(UINT nCmdID,
- __in REFPROPERTYKEY key,
- __in_opt const PROPVARIANT* ppropvarCurrentValue,
- __out PROPVARIANT* ppropvarNewValue)
- {
- UNREFERENCED_PARAMETER(ppropvarCurrentValue);
- HRESULT hr = E_FAIL;
- if (key == UI_PKEY_Label)
- {
- // 更新控件cmdPrintRelationships的標(biāo)簽文本
- if (nCmdID == cmdPrintRelationships)
- {
- hr = UIInitPropertyFromString(UI_PKEY_Label,
- L"New Label", ppropvarNewValue);
- }
- }
- return hr;
- }
現(xiàn)在,我們就可以編譯運(yùn)行整個(gè)解決方案,點(diǎn)擊相應(yīng)的按鈕控件,就可以看到對控件屬性修改的效果了:
圖3 修改控件屬性
到這里,我們完成了整個(gè)Ribbon歷程:從創(chuàng)建XML文件到添加宿主對象,從創(chuàng)建到初始化,從控件消息處理到控件屬性更新。現(xiàn)在,Ribbon界面對我們來說,已經(jīng)不再僅僅是微軟的一種界面技術(shù),她切切實(shí)實(shí)地來到了我們身邊,可以為我們所用,提高應(yīng)用程序的用戶體驗(yàn)。
擁抱Ribbon,擁抱Windows 7,擁抱Visual Studio 2010!
【編輯推薦】