Duilib教程-自动布局2

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

 

在项目中,咱们不多会100%的编写一个软件,特别是界面相关的,咱们会使用之前已经编写好的窗口,或网上的开源模块。举一个简单的例子来讲,若是你要编写一个视频播放器,关于视频的播放窗口,就用不着用DUI来实现,咱们彻底可使用网上的开源库,嵌入一个播放的WND便可(固然有的库也支持回调的方式,用户能够在本身的窗口中将回调出来的图片进行自由绘制)。ui

 

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

 

UIManager.cpp 第750行:spa

    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,重绘而已,它的大小并无设置为与窗口同样的大小。code

 

在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里面设置窗口位置,并不会达到效果。blog

 

那么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用处也很大,看状况使用了。

相关文章
相关标签/搜索