2.duilib消息处理简单理解

2021-08-02  本文已影响0人  _张鹏鹏_

基于之前对windows通信的大致了解网友Alberl的教程。我修改了下Alberl在他教程中的HelloWorld程序,添加了两个按键。

使用duilib时,一般在Notify里面处理各个控件的消息,HandleMessage里面一般是创建窗口。刚开始看到这个代码的时候,有几个疑问:

1.框架是怎么调用这两个接口的?

2.都是处理消息为啥搞两个接口,这两个有啥区别?

class CDuiFrameWnd : public CWindowWnd, private INotifyUI
{
public:
    virtual LPCTSTR GetWindowClassName() const
    {
        return _T("DUIMainFrame4444");
    }
    virtual void    Notify(TNotifyUI& msg)
    {
        if (msg.sType == _T("click"))
        {
            if (msg.pSender->GetName() == _T("btnHello1"))
            {
                ::MessageBox(NULL, _T("我是按钮1"),_T("点击了按钮1"),NULL);
            }

            if (msg.pSender->GetName() == _T("btnHello2"))
            {
                ::MessageBox(NULL, _T("我是按钮2"), _T("点击了按钮2"), NULL);
            }
        }
    }

    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        LRESULT lRes = 0;

        if (uMsg == WM_CREATE)
        {
            m_PaintManager.Init(m_hWnd);  // 关联ui管理器;  m_hWnd --->CreateWindowEx时操作系统分配的窗口句柄; 
            CControlUI *pVLayout = new CVerticalLayoutUI;
            pVLayout->SetName(_T("mainVertical"));
            pVLayout->SetBkColor(0xFF00FF00);
            m_PaintManager.AttachDialog(pVLayout); // CPaintManagerUI成员部分重新初始化操作
            m_PaintManager.AddNotifier(this);

            auto mainVertical = (DuiLib::CVerticalLayoutUI*)m_PaintManager.FindControl(_T("mainVertical"));

            // 
            CControlUI *pButton1 = new CButtonUI;
            pButton1->SetFloat();
            SIZE leftTop = { 30, 80};
            pButton1->SetFixedXY(leftTop);
            pButton1->SetFixedWidth(70);
            pButton1->SetFixedHeight(30);

            pButton1->SetText(_T("btnHello1"));   // 设置文字
            pButton1->SetBkColor(0xFF00FFFF);       // 设置背景色
            pButton1->SetName(_T("btnHello1"));
            mainVertical->Add(pButton1);

            //
            CControlUI *pButton2 = new CButtonUI;
            pButton2->SetFloat();
            SIZE leftTop1 = { 120, 80 };
            pButton2->SetFixedXY(leftTop1);
            pButton2->SetFixedWidth(70);
            pButton2->SetFixedHeight(40);

            pButton2->SetText(_T("btnHello2"));   // 设置文字
            pButton2->SetBkColor(0xFF00FFFF);       // 设置背景色
            pButton2->SetName(_T("btnHello2"));
            mainVertical->Add(pButton2);
            return lRes;
        }

        if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
        {
            return lRes;
        }
        return __super::HandleMessage(uMsg, wParam, lParam);
    }

protected:
    CPaintManagerUI m_PaintManager;
};

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    CPaintManagerUI::SetInstance(hInstance);  // 应用程序分配的实例句柄;

    CDuiFrameWnd duiFrame;
    // 注册窗口类、创建窗口实例;
    duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    duiFrame.ShowModal();  // 消息循环; 接收、处理windows消息
    return 0;
}

带着这些疑问,走读源码,梳理如下:

duiFrame.Create会进行窗口类注册窗口实例创建

HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
    if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
    if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
    m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
    ASSERT(m_hWnd!=NULL);
    return m_hWnd;
}

注册窗口过程的回调函数是CWindowWnd::__WndProc,当windows给窗口发送消息时会调用到这个回调函数;

这里CreateWindowEx的最后一个参数LPVOID lpParam被赋值为thisduiFrame对象的地址,这个十分重要,后面会用到;

这是窗口过程的代码:

LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CWindowWnd* pThis = NULL;
    if( uMsg == WM_NCCREATE ) {
        // 很重要;
        LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
        pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);  // pThis实际指向duiFrame
        pThis->m_hWnd = hWnd;
        ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
    } 
    else {
        // pThis实际指向duiFrame
        pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
        if( uMsg == WM_NCDESTROY && pThis != NULL ) {
            LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
            ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
            if( pThis->m_bSubclassed ) pThis->Unsubclass();
            pThis->m_hWnd = NULL;
            pThis->OnFinalMessage(hWnd);
            return lRes;
        }
    }
    if( pThis != NULL ) {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    } 
    else {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}

WM_NCCREATE消息在WM_CREATE消息之前发送,lParam携带LPCREATESTRUCT结构体地址,该结构体的成员lpCreateParams 为调用CreateWindowEx时传入的最后一个参数,即duiFrame对象的地址。拿到这个地址后,就能够调用CDuiFrameWnd类的HandleMessage方法。上述就是HandleMessage方法的调用过程。

在这个方法里面,我们处理了WM_CREATE消息,我们创建了一个垂直布局和两个按键。m_PaintManager.AddNotifier是添加控件等消息响应,这样消息就会传达到duilib的消息循环,我们可以在Notify函数里做消息处理。

m_PaintManager.AttachDialog(pVLayout)做了很多工作,其中InitControls用于设置控件的父节点和该控件所属的UI管理实例(即CPaintManagerUI)。

mainVertical->Add(pButton1);也是这个作用,这个很重要,Notify(TNotifyUI& msg)的调用依赖于这步配置。

    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        LRESULT lRes = 0;

        if (uMsg == WM_CREATE)
        {
            m_PaintManager.Init(m_hWnd);  // 关联ui管理器;  m_hWnd --->CreateWindowEx时操作系统分配的窗口句柄; 
            CControlUI *pVLayout = new CVerticalLayoutUI;
            pVLayout->SetName(_T("mainVertical"));
            pVLayout->SetBkColor(0xFF00FF00);
            m_PaintManager.AttachDialog(pVLayout); // CPaintManagerUI成员部分重新初始化操作
            m_PaintManager.AddNotifier(this);

            auto mainVertical = (DuiLib::CVerticalLayoutUI*)m_PaintManager.FindControl(_T("mainVertical"));

            // 
            CControlUI *pButton1 = new CButtonUI;
            pButton1->SetFloat();
            SIZE leftTop = { 30, 80};
            pButton1->SetFixedXY(leftTop);
            pButton1->SetFixedWidth(70);
            pButton1->SetFixedHeight(30);

            pButton1->SetText(_T("btnHello1"));   // 设置文字
            pButton1->SetBkColor(0xFF00FFFF);       // 设置背景色
            pButton1->SetName(_T("btnHello1"));
            mainVertical->Add(pButton1);

            //
            CControlUI *pButton2 = new CButtonUI;
            pButton2->SetFloat();
            SIZE leftTop1 = { 120, 80 };
            pButton2->SetFixedXY(leftTop1);
            pButton2->SetFixedWidth(70);
            pButton2->SetFixedHeight(40);

            pButton2->SetText(_T("btnHello2"));   // 设置文字
            pButton2->SetBkColor(0xFF00FFFF);       // 设置背景色
            pButton2->SetName(_T("btnHello2"));
            mainVertical->Add(pButton2);
            return lRes;
        }

        if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
        {
            return lRes;
        }
        return __super::HandleMessage(uMsg, wParam, lParam);
    }

当点击按键时,windows系统先后发送WM_LBUTTONDOWNWM_LBUTTONUP消息。消息被投递到窗口过程 CWindowWnd::__WndProcCDuiFrameWnd类的HandleMessage再次被调用,消息会投递到m_PaintManager.MessageHandler进行处理。

在这里,duilib会根据坐标的位置,得到控件实例。本例中即按钮控件对象的地址。按钮对象知道自己所属的UI和父控件(初始化时设置的)。这样就调用到Notify了。

因此Notify方法的调用,是先调用HandleMessage而来,然后在该方法里面再调回到Notify
个人感觉,从使用来说HandleMessage里面一般处理窗口、控件创建,Notify中处理各个控件产生的事件。

参考文献:

  1. duilib简明教程
  2. duilib进阶教程
  3. duilib使用心得
  4. duilib各种布局的作用,相对布局与绝对布局的的意义与用法_Redrain的专栏-CSDN博客
  5. Duilib教程-自动布局2 - 夜雨無聲 - 博客园 (cnblogs.com)
  6. duilib开发(六):基本控件介绍_yp18792574062的博客-CSDN博客_duilib radio
  7. duilib开发(十):动态添加控件_yp18792574062的博客-CSDN博客
  8. DUILIB UI创建过程 - Guozht - 博客园 (cnblogs.com
  9. duilib库框架介绍_架构师之路-CSDN博客_duilib框架
  10. DuiLib源码分析_万千知了的博客
上一篇下一篇

猜你喜欢

热点阅读