zoukankan      html  css  js  c++  java
  • Duilib教程-自动布局2

    在上一节中,我简单介绍了控件随父LAYOUT自由移动的设置。在这一节,我将介绍一种常见的情况:嵌入窗口。

    在项目中,我们很少会100%的编写一个软件,特别是界面相关的,我们会使用以前已经编写好的窗口,或网上的开源模块。举一个简单的例子来说,如果你要编写一个视频播放器,关于视频的播放窗口,就用不着用DUI来实现,我们完全可以使用网上的开源库,嵌入一个播放的WND即可(当然有的库也支持回调的方式,用户可以在自己的窗口中将回调出来的图片进行自由绘制)。

    我们需要在窗口大小改变时,即时地改变播放窗口的大小。也许你会说这非常简单,直接重载OnSize,然后获取占位控件(使用占位控件才是最正确的选择,如果在程序中判断左边距、右边距,就做不到UI、CODE分离了)的大小,然后设置即可。但是当你真正使用的时候,发现并没有那么简单。来看代码:

    UIManager.cpp 第750行:

        case WM_SIZE:
    
            {
    
                if( m_pFocus != NULL ) {
    
                    TEventUI event = { 0 };
    
                    event.Type = UIEVENT_WINDOWSIZE;
    
                    event.pSender = m_pFocus;
    
                    event.dwTimestamp = ::GetTickCount();
    
                    m_pFocus->Event(event);
    
                }
    
                if( m_pRoot != NULL ) m_pRoot->NeedUpdate();
    
            }
    
            return true;

    我们看到,窗口大小改变,ROOT只是简单的 NeedUpdate,重绘而已,它的大小并没有设置为与窗口一样的大小。

    在WinImplBase.cpp 第214 行:

    LRESULT WindowImplBase::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    
    {
    
             SIZE szRoundCorner = m_PaintManager.GetRoundCorner();
    
    #if defined(WIN32) && !defined(UNDER_CE)
    
             if( !::IsIconic(*this) && (szRoundCorner.cx != 0 || szRoundCorner.cy != 0) ) {
    
                       CDuiRect rcWnd;
    
                       ::GetWindowRect(*this, &rcWnd);
    
                       rcWnd.Offset(-rcWnd.left, -rcWnd.top);
    
                       rcWnd.right++; rcWnd.bottom++;
    
                       HRGN hRgn = ::CreateRoundRectRgn(rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, szRoundCorner.cx, szRoundCorner.cy);
    
                       ::SetWindowRgn(*this, hRgn, TRUE);
    
                       ::DeleteObject(hRgn);
    
             }
    
    #endif
    
             bHandled = FALSE;
    
             return 0;
    
    }

    也是啥也没做。

    所以在OnSize里面设置窗口位置,并不会达到效果。

    那么DUILIB是在哪里设置ROOT的大小呢?UIManager.cpp 第 615行,即在WM_PAINT中进行设置。

    m_pRoot->SetPos(rcClient);

    1.SetPos

    当你看到这里时,我想你已经知道第一种方法了。即在 OnSize中,

    RECT rc;
    
    GetClientRect (m_hWnd, &rc);
    
    m_PaintManager.GetRoot()->SetPos (rc);
    
    const RECT& rc_pos = targer_ui_->GetPos ();
    
    ::MoveWindow (move_wnd, rc_pos.left, rc_pos.top, rc_pos.right – rc_pos.left, rc_pos.bottom – rc_pos.top, TRUE);

    即我们主动设置大小,ROOT设置了POS后,会将它的子控件也设置POS,详情请看源码。所以,我们就能够得到正确的位置信息了。

    但是这并不是最好的方式,原因很简单,OnSize会被频繁的调用,特别是在程序初始化的时候,OnSize被调用N次,而且在最小化的时候也会被调用。而且当你看1.SetPos时,你也猜到了会有第二种方式了。

    2.委托 OnSize

    假设我们的占位控件为 target_ui_,它有一个委托成员变量:OnSize。直接看代码吧:

    target_ui_->OnSize += MakeDelegate (this, &CYourWnd::OnTargetSizeChanged);
    
     
    
    bool CYourWnd:: OnTargetSizeChanged (void* param)
    
    {
    
    const RECT& rc_pos = targer_ui_->GetPos ();
    
    ::MoveWindow (move_wnd, rc_pos.left, rc_pos.top, rc_pos.right – rc_pos.left, rc_pos.bottom – rc_pos.top, TRUE);
    
    }

    如此简单,又如此优美的代码。

    注意使用的是 +=

    在这里,我们也看到了作者自己实现了委托的编写(我不清楚是不是使用了开源库),可见作者的C++功底是相当深厚的。

    看CControlUI的源码,你会发现如下委托对象:

    public:
    
        CEventSource OnInit;
    
        CEventSource OnDestroy;
    
        CEventSource OnSize;
    
        CEventSource OnEvent;
    
        CEventSource OnNotify;

    顾名思义,无需赘述。

    这里说一下Event和Notify的区别。

    Event是控件自己收到的消息,比如鼠标左键按下、弹起、双击等,DUILIB先向控件自己发一个事件。

    Notify通知,是向WND发送的通知消息,类似MFC中对话框收到控件的NOTIFY(包括按钮的单击),它默认情况下是由窗口接收的,在窗口的Notify函数中进行响应。

    DUILIB的处理流程是,先向CONTROL发送事件,然后向WND发送通知。

    OnNotify相当有用,因为你可以定制每个控件的响应,而不需要在WND的Notify中进行一大堆的if..else..了。

    OnEvent用处也很大,看情况使用了。

  • 相关阅读:
    ASP.NET页面事件执行过程 总结
    程序员最应该读的图书(中译版) [收藏]
    C# 中的委托和事件的详解资料
    已添加项。字典中的关键字
    TFS 删除团队项目集合
    注册后第一篇
    类型的权限已失败 SqlClientPermission
    C#创建Oracle存储过程
    使用MySQL with 递归查询菜单树
    MySQL 常用TSQL(持续更新...)
  • 原文地址:https://www.cnblogs.com/lin1270/p/4209597.html
Copyright © 2011-2022 走看看