DuiLib(三)——控件消息

上一篇講了控件建立,這篇說說控件消息。directui的中心思想是在真實窗口之上畫出全部控件,那這些控件是如何獲取各自消息的?html

經過第一篇的示例能夠看到窗口消息的傳遞過程:windows

  1. CWindowWnd::__WndProc
  2. CWindowWnd::HandleMessage(CFrameWindowWnd類覆蓋此函數)
  3. CPaintManagerUI::MessageHandler

消息最終傳遞到CPaintManagerUI::MessageHandler中,以WM_LBUTTONDOWN消息爲例,看看消息如何分發到控件app

。。。

1
case WM_LBUTTONDOWN: 2 { 3 // We alway set focus back to our app (this helps 4 // when Win32 child windows are placed on the dialog 5 // and we need to remove them on focus change). 6 ::SetFocus(m_hWndPaint); 7 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 8 m_ptLastMousePos = pt; 9 CControlUI* pControl = FindControl(pt);//查找控件 10 if( pControl == NULL ) break; 11 if( pControl->GetManager() != this ) break; 12 m_pEventClick = pControl; 13 pControl->SetFocus(); 14 SetCapture(); 15 TEventUI event = { 0 }; 16 event.Type = UIEVENT_BUTTONDOWN; 17 event.pSender = pControl; 18 event.wParam = wParam; 19 event.lParam = lParam; 20 event.ptMouse = pt; 21 event.wKeyState = (WORD)wParam; 22 event.dwTimestamp = ::GetTickCount(); 23 pControl->Event(event);//事件--->控件 24 } 25 break;

。。。

能夠清楚地看到,分發消息分爲兩步:框架

  1. 查找控件:CPaintManagerUI::FindControl
  2. 調用控件接口:CControlUI::Event

如今看看控件查找是怎麼回事:函數

1 CControlUI* CPaintManagerUI::FindControl(POINT pt) const
2 {
3     ASSERT(m_pRoot);
4     return m_pRoot->FindControl(__FindControlFromPoint, &pt, UIFIND_VISIBLE | UIFIND_HITTEST | UIFIND_TOP_FIRST);
5 }

m_pRoot看名字應該是根控件了,在第二篇的控件建立過程當中CDialogBuilder::Create返回了根控件,而且經過CPaintManagerUI::AttachDialog將根控件指針保存在了m_pRoot中。看看xml文件ui

1 <?xml version="1.0" encoding="UTF-8"?>
2 <Window mininfo="400,240" size="400,240" alpha="250">
3     <Font name="微軟雅黑" size="18" bold="true" default="true"/>    
4     <VerticalLayout inset="10,6,10,6" bkimage="back.jpg">
5     <Label name="label" float="true" pos="10,10,100,30" bkcolor="#FF00FFFF" textcolor="#FFFFFFFF" text="Label"/>
6     </VerticalLayout>
7 </Window>

根控件爲VerticalLayout,是CContainerUI控件容器子類。而CContainerUI又是CControlUI子類。建立控件時,全部的子控件都添加到了根控件容器中,查找控件天然經過根控件容器實現。this

 1 CControlUI* CContainerUI::FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags)
 2 {
 3     。。。
 4 
 5     if( (uFlags & UIFIND_TOP_FIRST) != 0 ) {
 6         for( int it = m_items.GetSize() - 1; it >= 0; it-- ) {
 7             CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);  8             if( pControl != NULL ) {
 9                 if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
10                     continue;
11                 else 
12                     return pControl;
13             }            
14         }
15     }
16     else {
17         for( int it = 0; it < m_items.GetSize(); it++ ) {
18             CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags); 19             if( pControl != NULL ) {
20                 if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
21                     continue;
22                 else 
23                     return pControl;
24             } 
25         }
26     }
27 
28 。。。
29 }

 容器CContainerUI的成員m_items中保存了全部子控件指針,經過遍歷子控件(調用CControlUI::FindControl)查找符合條件的子控件,還能夠根據標識uFlags進行不一樣的查找方式。spa

查找到子控件後,再看看控件的事件接口CControlUI::Event。很明顯這是一個虛函數,經過多態,各子控件能夠實現本身的事件處理函數。好比控件CButtonUI能夠處理UIEVENT_KEYDOWN、UIEVENT_BUTTONUP事件,更新按鈕狀態。指針

至此,控件消息分發介紹完了。那如何添加控件事件處理?答案是經過INotifyUI接口。在第一篇的示例中,框架類CFrameWindowWnd不光繼承了窗口類CWindowWnd,也實現了INotifyUI接口。INotifyUI很簡單code

1 class INotifyUI
2 {
3 public:
4     virtual void Notify(TNotifyUI& msg) = 0;
5 };

接口參數爲結構體TNotifyUI

1 typedef struct tagTNotifyUI 
2 {
3     CStdString sType;//事件類型
4     CControlUI* pSender;//控件
5     DWORD dwTimestamp;
6     POINT ptMouse;
7     WPARAM wParam;
8     LPARAM lParam;
9 } TNotifyUI;

框架類只要實現這個接口,並註冊(CPaintManagerUI::AddNotifier),就能夠監聽控件事件了。這是典型的觀察者模式。再看看第一篇給出的示例代碼。

 1 class CFrameWindowWnd : public CWindowWnd, public INotifyUI  2 {
 3 public:
 4     CFrameWindowWnd() { };
 5     LPCTSTR GetWindowClassName() const { return _T("FrameWnd"); };
 6     UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
 7     void OnFinalMessage(HWND /*hWnd*/) { delete this; };
 8 
 9     void Notify(TNotifyUI& msg) 10  { 11         if( msg.sType == _T("windowinit") ) { 12  } 13         else if( msg.sType == _T("click") ) { 14  } 15  } 16 
17     //消息處理:窗口函數__WndProc ---> HandleMessage
18     LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
19     {
20         if( uMsg == WM_CREATE ) {
21             m_pm.Init(m_hWnd);
22             //根據XML建立控件
23             CDialogBuilder builder;
24             CControlUI* pRoot = builder.Create(_T("HelloWorld.xml"), (UINT)0, NULL, &m_pm);
25             ASSERT(pRoot && "Failed to parse XML");
26             m_pm.AttachDialog(pRoot);
27             m_pm.AddNotifier(this); 28             return 0;
29         }
30         else if( uMsg == WM_DESTROY ) {
31             ::PostQuitMessage(0L);
32         }
33         else if( uMsg == WM_ERASEBKGND ) {
34             return 1;
35         }
36 
37         //消息處理:CPaintManagerUI::MessageHandler
38         LRESULT lRes = 0;
39         if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
40         return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
41     }
42 
43 public:
44     CPaintManagerUI m_pm;
45 };

關於控件消息基本就這些了,有些地方沒有細究,先了解大致流程比較重要。

相關文章
相關標籤/搜索