在上一节中,我简单介绍了控件随父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用处也很大,看状况使用了。