duilib学习领悟(4)
使用duilib創建的主窗口繪制工作全都發生在一個 真實存在的主窗口句柄當中,這個繪制過程稍稍有些復雜,但再復雜也逃不過WM_PAINT消息,所有的繪制工作也都由這個WM_PAINT消息完成.在上幾篇中,我們總結了,所有的窗口消息都被CPaintManager類攔截處理,它成為所有消息的路由中心,我們看看WM_PAINT消息的處理:
case WM_PAINT:{// Should we paint?RECT rcPaint = { 0 };if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;if( m_pRoot == NULL ) {PAINTSTRUCT ps = { 0 };::BeginPaint(m_hWndPaint, &ps);::EndPaint(m_hWndPaint, &ps);return true;} // Do we need to resize anything?// This is the time where we layout the controls on the form.// We delay this even from the WM_SIZE messages since resizing can be// a very expensize operation.if( m_bUpdateNeeded ) {m_bUpdateNeeded = false;RECT rcClient = { 0 };::GetClientRect(m_hWndPaint, &rcClient);if( !::IsRectEmpty(&rcClient) ) {if( m_pRoot->IsUpdateNeeded() ) {m_pRoot->SetPos(rcClient);if( m_hDcOffscreen != NULL ) ::DeleteDC(m_hDcOffscreen);if( m_hDcBackground != NULL ) ::DeleteDC(m_hDcBackground);if( m_hbmpOffscreen != NULL ) ::DeleteObject(m_hbmpOffscreen);if( m_hbmpBackground != NULL ) ::DeleteObject(m_hbmpBackground);m_hDcOffscreen = NULL;m_hDcBackground = NULL;m_hbmpOffscreen = NULL;m_hbmpBackground = NULL;}else {CControlUI* pControl = NULL;while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) {pControl->SetPos( pControl->GetPos() );}}// We'll want to notify the window when it is first initialized// with the correct layout. The window form would take the time// to submit swipes/animations.if( m_bFirstLayout ) {m_bFirstLayout = false;SendNotify(m_pRoot, DUI_MSGTYPE_WINDOWINIT, 0, 0, false);}}}// Set focus to first control?if( m_bFocusNeeded ) {SetNextTabControl();}//// Render screen//// Prepare offscreen bitmap?if( m_bOffscreenPaint && m_hbmpOffscreen == NULL ){RECT rcClient = { 0 };::GetClientRect(m_hWndPaint, &rcClient);m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint);m_hbmpOffscreen = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); ASSERT(m_hDcOffscreen);ASSERT(m_hbmpOffscreen);}// Begin Windows paintPAINTSTRUCT ps = { 0 };::BeginPaint(m_hWndPaint, &ps);if( m_bOffscreenPaint ){HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(m_hDcOffscreen, m_hbmpOffscreen);int iSaveDC = ::SaveDC(m_hDcOffscreen);if( m_bAlphaBackground ) {if( m_hbmpBackground == NULL ) {RECT rcClient = { 0 };::GetClientRect(m_hWndPaint, &rcClient);m_hDcBackground = ::CreateCompatibleDC(m_hDcPaint);;m_hbmpBackground = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); ASSERT(m_hDcBackground);ASSERT(m_hbmpBackground);::SelectObject(m_hDcBackground, m_hbmpBackground);::BitBlt(m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,ps.rcPaint.bottom - ps.rcPaint.top, ps.hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);}else::SelectObject(m_hDcBackground, m_hbmpBackground);::BitBlt(m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,ps.rcPaint.bottom - ps.rcPaint.top, m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);}//繪制控件m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint);for( int i = 0; i < m_aPostPaintControls.GetSize(); i++ ) {CControlUI* pPostPaintControl = static_cast<CControlUI*>(m_aPostPaintControls[i]);pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint);}::RestoreDC(m_hDcOffscreen, iSaveDC);::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,ps.rcPaint.bottom - ps.rcPaint.top, m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);::SelectObject(m_hDcOffscreen, hOldBitmap);if( m_bShowUpdateRect ) {HPEN hOldPen = (HPEN)::SelectObject(ps.hdc, m_hUpdateRectPen);::SelectObject(ps.hdc, ::GetStockObject(HOLLOW_BRUSH));::Rectangle(ps.hdc, rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom);::SelectObject(ps.hdc, hOldPen);}}else{// A standard paint jobint iSaveDC = ::SaveDC(ps.hdc);//繪制控件m_pRoot->DoPaint(ps.hdc, ps.rcPaint);::RestoreDC(ps.hdc, iSaveDC);}// All Done!::EndPaint(m_hWndPaint, &ps);}// If any of the painting requested a resize again, we'll need// to invalidate the entire window once more.if( m_bUpdateNeeded ) {::InvalidateRect(m_hWndPaint, NULL, FALSE);}return true;
所有的繪制工作都會回到基類CControlUI的DoPaint() 代碼如下:
void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint) {if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;// 繪制循序:背景顏色->背景圖->狀態圖->文本->邊框if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) {CRenderClip roundClip;CRenderClip::GenerateRoundClip(hDC, m_rcPaint, m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip);PaintBkColor(hDC);PaintBkImage(hDC);PaintStatusImage(hDC);PaintText(hDC);PaintBorder(hDC);}else {PaintBkColor(hDC);PaintBkImage(hDC);PaintStatusImage(hDC);PaintText(hDC);PaintBorder(hDC);} }但是,這里又引出一個問題,繪制工作是如何路由到CControlUI的DoPaint()中的呢,如果程序中比較簡單,就像我們現在這樣,沒有用到容器,只有一個按鈕,那么就相對簡單,調用m_pRoot->DoPaint()直到調用的就是基類的DoPaint():
假如我們的窗口中,使用的控件不只一個,必然用到容器,這時候再調用m_pRoot->DoPaint()就不是直接調用CControlUI::DoPaint()而是調用的容器類的DoPaint(),來看代碼:
void CContainerUI::DoPaint(HDC hDC, const RECT& rcPaint){RECT rcTemp = { 0 };if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return;CRenderClip clip;CRenderClip::GenerateClip(hDC, rcTemp, clip);CControlUI::DoPaint(hDC, rcPaint);if( m_items.GetSize() > 0 ) {RECT rc = m_rcItem;rc.left += m_rcInset.left;rc.top += m_rcInset.top;rc.right -= m_rcInset.right;rc.bottom -= m_rcInset.bottom;if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();if( !::IntersectRect(&rcTemp, &rcPaint, &rc) ) {for( int it = 0; it < m_items.GetSize(); it++ ) {CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);if( !pControl->IsVisible() ) continue;if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;if( pControl ->IsFloat() ) {if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;pControl->DoPaint(hDC, rcPaint);}}}else {CRenderClip childClip;CRenderClip::GenerateClip(hDC, rcTemp, childClip);for( int it = 0; it < m_items.GetSize(); it++ ) {CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);if( !pControl->IsVisible() ) continue;if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;if( pControl ->IsFloat() ) {if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;CRenderClip::UseOldClipBegin(hDC, childClip);pControl->DoPaint(hDC, rcPaint);CRenderClip::UseOldClipEnd(hDC, childClip);}else {if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue;pControl->DoPaint(hDC, rcPaint);}}}}if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) {if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) {m_pVerticalScrollBar->DoPaint(hDC, rcPaint);}}if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) {m_pHorizontalScrollBar->DoPaint(hDC, rcPaint);}}}
控件容器繪制完自己后,遍歷子控件(包括子控件容器)調用其DoPaint,完成子控件繪制(代碼已經在上上面貼出);
最終的繪制都是通過渲染引擎CRenderEngine實現的。
這樣看來,整個繪制思路還是很清晰的:CPaintManagerUI::MessageHandler(WM_PAINT)--->CContainerUI::DoPaint--->CControlUI::DoPaint--->CRenderEngine。
OK繪制工作完成!
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/xiejiulong/p/3793005.html
總結
以上是生活随笔為你收集整理的duilib学习领悟(4)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c# TCP Socket通讯基础
- 下一篇: SPOJ_SUBLEX