duilib学习领悟(4)
使用duilib创建的主窗口绘制工作全都发生在一个 真实存在的主窗口句柄当中,这个绘制过程稍稍有些复杂,但再复杂也逃不过WM_PAINT消息,所有的绘制工作也都由这个WM_PAINT消息完成.在上几篇中,我们总结了,所有的窗口消息都被CPaintManager类拦截处理,它成为所有消息的路由中心,我们看看WM_PAINT消息的处理:
case WM_PAINT:
{
// Should we paint?
RECT rcPaint = { };
if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;
if( m_pRoot == NULL ) {
PAINTSTRUCT ps = { };
::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 = { };
::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, , , false);
}
}
}
// Set focus to first control?
if( m_bFocusNeeded ) {
SetNextTabControl();
}
//
// Render screen
//
// Prepare offscreen bitmap?
if( m_bOffscreenPaint && m_hbmpOffscreen == NULL )
{
RECT rcClient = { };
::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 paint
PAINTSTRUCT ps = { };
::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 = { };
::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 = ; 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 job
int 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 > || m_cxyBorderRound.cy > ) {
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 = { };
if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return; CRenderClip clip;
CRenderClip::GenerateClip(hDC, rcTemp, clip);
CControlUI::DoPaint(hDC, rcPaint); if( m_items.GetSize() > ) {
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 = ; 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 = ; 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绘制工作完成!
duilib学习领悟(4)的更多相关文章
- duilib学习领悟(3)
世上本无窗口,窗口只是人的眼睛和电脑屏幕及鼠标键盘相互操作后的视觉效果! 下面我们来看看我们之前讲过的代码: class CDuiFrameWnd : public CWindowWnd, publi ...
- duilib学习领悟(1)
学习duilib已经有一段时间,一直没时间写总结,今天得出空来,写写心得体会! 由于本人知识有限,若有错误地方,望批评指正.多谢.! 初识duilib 刚开始接触duilib的时候,觉的他好神奇,整个 ...
- duilib学习领悟(2)
再次强调,duilib只不过是一种思想! 在上一节中,我剖析了duilib中窗口类的注册,其中遗留两个小问题没有细说的? 第一个问题:过程函数中__WndProc()中有这么一小段代码: pThis ...
- DuiLib学习笔记(二) 扩展CScrollbar属性
DuiLib学习笔记(二) 扩展CScrollbar属性 Duilib的滚动条滑块默认最小值为滚动条的高度(HScrollbar)或者宽度(VScrollbar).并且这个值默认为16.当采用系统样式 ...
- Duilib学习笔记《06》— 窗体基类WindowImpBase
在前面的例子中我们发现,窗口都是继承CWindowWnd.INotifyUI,然后重载相关函数去实现.显然,我们发现窗口的创建流程实际上都是差不多的,主要只是在OnCreate加载的配置文件不同等等… ...
- Duilib学习笔记《05》— 消息响应处理
在Duilib学习笔记<04>中已经知道了如何将窗体显示出来,而如何处理窗体上的事件.消息呢? 一. 系统消息 窗体显示的时候我们就已经说了,窗体是继承CWindowWnd类的,对于窗体的 ...
- Duilib学习笔记《04》— 窗体显示
在前面已经了解了duilib控件以及界面布局相关内容,接下来就要考虑该如何将xml中描述的布局通过界面展现出来.实际上在 Duilib学习笔记<01> 中我们已经简单提到过基本的流程及元素 ...
- Duilib学习笔记《03》— 控件使用
在前面已经对duilib有个一个基本的了解,并且创建了简单的空白窗体.这仅仅只是一个开始,如何去创建一个绚丽多彩的界面呢?这就需要一些控件元素(按钮.文本框.列表框等等)来完善. 一. Duilib控 ...
- duilib学习 --- 360demo 学习
我想通过360demo的学习,大概就能把握duilib的一般用法,同时引申出一些普遍问题,和普遍解决方法.并在此分享一些链接和更多内容的深入学习..... 原谅我是一个菜鸟,什么都想知道得清清楚楚.. ...
随机推荐
- redis相关操作命令
一.相关命令列表: 命令原型 时间复杂度 命令描述 返回值 KEYS pattern O(N) 时间复杂度中的N表示数据库中Key的数量.获取所有匹配pattern参数的Keys.需要说明的是,在我们 ...
- 日常工作问题解决:redhat6.9--解决yum功能不能正常使用和配置yum源
1.问题描述 解决RedHat6.9下yum功能不能用问题: 在redhat6.9下使用yum安装时,会提示:This system is not registered to Red Hat Subs ...
- 日常工作问题解决:centos7下使用yum安装软件报yum.pid锁定
问题描述: 在centos7下使用yum进行软件安装时报yum.pid已经被锁定,如下所示: [root@centos7-129 ~]# yum -y install dhcp 已加载插件:faste ...
- Java基础---Java变量
变量:程序运行期间,内容可以发生改变的量. 创建一个变量并且使用的格式: 数据类型 变量名称; // 创建了一个变量 变量名称 = 数据值; // 赋值,将右边的数 ...
- 方法区(关于java虚拟机内存的那些事)
<深入理解 java 虚拟机> 读书扩展 作者:淮左白衣 写于 2018年4月13日21:26:05 目录 方法区 图例(方法区中都保存什么) 类型信息 类型的常量池 (即运行时常量池) ...
- php微信小程序生成二维码,出现乱码,{"errcode":44002,"errmsg":"empty post data"},'{"errcode":41001,"errmsg":"access_token missing hint: [OoC.2a0822e255]"}',以及其他的坑
微信小程序的坑简直比我的头发还多,今天下午获取微信二维码,遇到的44002,41001,还有图片没法正常显示以及小程序参数获取的问题,接下来一一解决 首先获取微信token然后再获取二维码这个没什么好 ...
- Hystrix的介绍(断路、降级)
在大中型分布式系统中,通常系统很多依赖(HTTP,hession,Netty,Dubbo等),如下图: 在高并发访问下,这些依赖的稳定性与否对系统的影响非常大,但是依赖有很多不可控问题:如网络 ...
- atomikos 优化JDBC性能
JDBC performance comes for free with our pooling DataSource classes: AtomikosDataSourceBean for XA-e ...
- hdu 6153 思维+扩展kmp
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6153 扩展kmp不理解的看下:http://www.cnblogs.com/z1141000271/p ...
- NHiberante从.net framework转移到.net standard(.net core 2.2)时遇到的坑及填坑
在.net framework中的创建session代码先贴一个 public class SessionBuilder { private static ISessionFactory _sessi ...