zoukankan      html  css  js  c++  java
  • DuiLib(三)——控件消息

    上一篇讲了控件创建,这篇说说控件消息。directui的中心思想是在真实窗口之上画出所有控件,那这些控件是如何获取各自消息的?

    通过第一篇的示例可以看到窗口消息的传递过程:

    1. CWindowWnd::__WndProc
    2. CWindowWnd::HandleMessage(CFrameWindowWnd类覆盖此函数)
    3. CPaintManagerUI::MessageHandler

    消息最终传递到CPaintManagerUI::MessageHandler中,以WM_LBUTTONDOWN消息为例,看看消息如何分发到控件

    。。。

    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文件

    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子类。创建控件时,所有的子控件都添加到了根控件容器中,查找控件自然通过根控件容器实现。

     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进行不同的查找方式。

    查找到子控件后,再看看控件的事件接口CControlUI::Event。很明显这是一个虚函数,通过多态,各子控件可以实现自己的事件处理函数。比如控件CButtonUI可以处理UIEVENT_KEYDOWN、UIEVENT_BUTTONUP事件,更新按钮状态。

    至此,控件消息分发介绍完了。那如何添加控件事件处理?答案是通过INotifyUI接口。在第一篇的示例中,框架类CFrameWindowWnd不光继承了窗口类CWindowWnd,也实现了INotifyUI接口。INotifyUI很简单

    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 };

    关于控件消息基本就这些了,有些地方没有细究,先了解大体流程比较重要。

  • 相关阅读:
    《算法图解》——第六章 广度有限搜索
    《算法图解》——第一章 算法简介
    《算法图解》——第二章 选择排序
    go-json处理的问题
    Go断言
    Go Example--格式化字符串
    Go Example--strings
    Go Example--组合函数
    Go Example--defer
    Go Example--panic
  • 原文地址:https://www.cnblogs.com/dahai/p/3457026.html
Copyright © 2011-2022 走看看