duilib整体的操作流程

刚开始的时候设置

CPaintManagerUI::SetInstance(hInstance);
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());
::CoInitialize(NULL);

这里设置的resourcepath是接下去图片资源的位置。

并初始化了COM库,并且在退出的时候要执行::CoUninitialize();

CMainWndDlg* pMainDlg = new CMainWndDlg();
pMainDlg->Create(NULL, _T("cxiaoln窗体"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
pMainDlg->CenterWindow();
pMainDlg->ShowModal();

接下去是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;
}
View Code

这里首先要判断的是否是SuperClassName,关于这个SuperClassName我们在CEditUI中已经说明了。如果是系统本身自带的控件则使用SuperClassName,如果自己创建的窗口则调用普通的GetWindowClassName

注册完成窗口之后,接下去就是创建窗口::CreateWindowEx

再接下去就是ShowModal,进行消息的循环。其实从ShowModal 我们发现它的消息循环跟CPaintManagerUI::MessageLoop();的消息循环是差不多的。消息都要先让CPaintManagerUI::TranslateMessage(&msg)进行处理,如果没有对应的控件进行处理才进入

::TranslateMessage(&msg);
::DispatchMessage(&msg);

进行消息处理。

为什么要这样子呢? 其实我们稍微分析一下就知道了,我们在创建窗口的时候创建的其实只是一个面板,然后具体的界面上面的按钮其实只是一些图片。或者颜色的绘制。具体这些界面上面的空间如何或者响应待会再讲。

先说这里的ShowModal则会开始调用消息。

按创建窗体一般的思路就传递WM_CREATE的消息,然后再传递WM_SIZE消息。pThis->HandleMessage(uMsg, wParam, lParam);

所以就会调用

首先创建窗口:OnCreate

LRESULT CMainWndDlg::OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    m_PaintManager.Init(m_hWnd);

    CDialogBuilder builder;
    CControlUI* pRoot = builder.Create(_T("DemoSkin.xml"), (UINT)0, NULL, &m_PaintManager);   // DemoSkin.xml需要放到exe目录下
    ASSERT(pRoot && "Failed to parse XML");

    m_PaintManager.AttachDialog(pRoot);
    m_PaintManager.AddNotifier(this);   // 添加控件等消息响应,这样消息就会传达到duilib的消息循环,我们可以在Notify函数里做消息处理

    Init();

    return 0;
}
View Code

这里首先进行了m_PaintManager.Init(m_hWnd);先进行了初始化,

void CPaintManagerUI::Init(HWND hWnd)
{
    ASSERT(::IsWindow(hWnd));
    // Remember the window context we came from
    m_hWndPaint = hWnd;
    m_hDcPaint = ::GetDC(hWnd);
    // We'll want to filter messages globally too
    m_aPreMessages.Add(this);
}
View Code

这里将m_PaintManager添加进了m_aPreMessages的数组。当在调用PreMessageHandler时,会先调用相关的有继承IMessageFilterUI的UI,但是我们发现这个数组是静态的,如果进行多线程的操作时候很有可能会出现多线程不安全的情况。所以多线程的时候得慎用。

在Init之后则会使用CDialogBuilder类进行了创建,builder.Create(xml),即解析xml函数,具体如何解析在上一章中已经说明了。

然后这里的AttachDialog,顾名思义就是将pRoot跟CPaintManager关联起来,其实主要是获取pRoot的各种参数(里面保存了xml文件解析的各种数据)。

接下去如果有继承INotify类,则这里可以添加AddNotifier(this),从而,当有事件发生并发送了SendNotify的时候,OnNotify函数则会收到相应的消息。具体发送的消息内容则根据每个UI控件。

===================================

那每个控件是如何响应的呢?

其实原理很简单,就是点击鼠标的时候,根据鼠标所在的位置当前的控件是什么FindControl(pt)来进行响应该操作对应的事件。具体可以看bool CPaintManagerUI::MessageHandler

如果出现重叠控件怎么办?

其实在最开始解析xml的时候已经给了答案了,它在将控件放进容器控件中时是按顺序存放的,当它在滚动台哦还有其他参数的方式进行查找还是没找到时候,则会进行如下方式的操作

if( (uFlags & UIFIND_TOP_FIRST) != 0 ) {
            for( int it = m_items.GetSize() - 1; it >= 0; it-- ) {
                CControlUI* pControl = static_cast<CControlUI*>(m_items[it])->FindControl(Proc, pData, uFlags);
                if( pControl != NULL ) {
                    if( (uFlags & UIFIND_HITTEST) != 0 && !pControl->IsFloat() && !::PtInRect(&rc, *(static_cast<LPPOINT>(pData))) )
                        continue;
                    else 
                        return pControl;
                }            
            }
        }
View Code

是从后往前进行遍历查找,即使同一个位置有两个控件它也能很容易的找到最后放进去的那个(isfloat)

原文地址:https://www.cnblogs.com/cxiaoln/p/4412935.html