SOUI中提供了大部分常用的win32標准控件的實現,如pushbutton, checkbox, radiobox, edit, richedit, listbox, combobox, treectrl, listctrl (report), hotkeyctrl等。
大部分控件在接收用戶輸入后,會發生狀態的改變,並以事件的形式傳遞給UI的所有者。
在SOUI中提供了兩種處理事件的方式:
1、在SHostWnd的派生類中重載
virtual BOOL SHostWnd::_HandleEvent(SOUI::EventArgs *pEvt){return FALSE;}
為了更方便的處理事件,SOUI提供了一組宏來構造這個事件處理函數,從而提供一種類似消息映射的事件處理形式。
如demo的CMainDlg中的實現:
//UI控件的事件及響應函數映射表 EVENT_MAP_BEGIN() EVENT_ID_COMMAND(1, OnClose) EVENT_ID_COMMAND(2, OnMaximize) EVENT_ID_COMMAND(3, OnRestore) EVENT_ID_COMMAND(5, OnMinimize) EVENT_NAME_CONTEXTMENU(L"edit_1140",OnEditMenu) EVENT_NAME_COMMAND(L"btn_msgbox",OnBtnMsgBox) EVENT_NAME_COMMAND(L"btnSelectGif",OnBtnSelectGIF) EVENT_NAME_COMMAND(L"btn_menu",OnBtnMenu) EVENT_NAME_COMMAND(L"btn_webkit_go",OnBtnWebkitGo) EVENT_NAME_COMMAND(L"btn_webkit_back",OnBtnWebkitBackward) EVENT_NAME_COMMAND(L"btn_webkit_fore",OnBtnWebkitForeward) EVENT_NAME_COMMAND(L"btn_webkit_refresh",OnBtnWebkitRefresh) EVENT_NAME_COMMAND(L"btn_hidetst",OnBtnHideTest) EVENT_NAME_COMMAND(L"btn_insert_gif",OnBtnInsertGif2RE) EVENT_MAP_END()
上面的EVENT_MAP_BEGIN()和EVENT_MAP_END()結合構造出一個_HandleEvent函數的實現,具體可以自己展開這兩個宏查看代碼。
同時SOUI也提供了一組解析SOUI::EventArgs *pEvt的宏,如上例中的EVENT_NAME_COMMAND, EVENT_ID_COMMAND等。
幫助用戶直接從控件的name或者ID屬性映射到消息響應函數。
這種事件響應方式最大的好處是能夠集中處理事件的分發,方便閱讀代碼,同時也和傳統的MFC,WTL的編程風格類似,降低用戶的學習成本。
2、采用事件訂閱的方式響應控件事件
雖然事件映射表提供了一種簡單有效的事件響應機制,由於事件映射表是一種編譯期形成的靜態的映射表,對於在運行期動態創建的控件的事件響應就無能為力了。
在MFC中,程序員通過要重載窗口類的DefWindowProc來處理運行期間動態創建的控件發來的消息。
這種方式靈活性夠了,但是不夠優雅,要在一個函數里做大量的swich分枝,導致這個處理函數很難維護。
設計模式里的觀察者模式可以比較好的解決這個問題。
為些在SOUI中我提供了一種事件訂閱的事件處理模式。
我們先看一下demo中怎樣處理列表控件的表頭點擊來執行排序操作:
void CMainDlg::InitListCtrl() { //找到列表控件 SListCtrl *pList=FindChildByName2<SListCtrl>(L"lc_test"); if(pList) { //列表控件的唯一子控件即為表頭控件 SWindow *pHeader=pList->GetWindow(GSW_FIRSTCHILD); //向表頭控件訂閱表明點擊事件,並把它和OnListHeaderClick函數相連。 pHeader->GetEventSet()->subscribeEvent(EVT_HEADER_CLICK,Subscriber(&CMainDlg::OnListHeaderClick,this)); //省略列表初始化代碼 } } //表頭點擊事件處理函數 bool CMainDlg::OnListHeaderClick(EventArgs *pEvtBase) { //事件對象強制轉換 EventHeaderClick *pEvt =(EventHeaderClick*)pEvtBase; SHeaderCtrl *pHeader=(SHeaderCtrl*)pEvt->sender; //從表頭控件獲得列表控件對象 SListCtrl *pList= (SListCtrl*)pHeader->GetParent(); //列表數據排序 SHDITEM hditem; hditem.mask=SHDI_ORDER; pHeader->GetItem(pEvt->iItem,&hditem); pList->SortItems(funCmpare,&hditem.iOrder); return true; }
通過事件訂閱可以在運行時方便的將一個控件的事件關聯到一個處理函數上,當然也可以隨時取消訂閱。
同時事件訂閱也是在腳本中響應控件事件的唯一方式(關於在SOUI中使用LUA腳本將在后續講解)。