在上一節中,我簡單介紹了控件隨父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用處也很大,看情況使用了。