从零开始的WTL入门教程(3)WTL窗口绘图,双重缓冲

由于WinApi诞生之时还没有Material Design这样美观的视觉表达规范,其系统控件样式相当的匮乏且充满工程师设计风格,因此大多数时候,控件都需要实现自定义绘图,即使只是简单的设置背景颜色。
完成了上面最简单的视窗控件后,我们来给它添加一个背景色。
绘图方法在系统更新控件时被调用。因此它也依赖于消息循环。我们可以在消息的定义文件中找到它。
绘图消息
OnClose一样的添加方式
添加OnPaint
该方法的参数 CDCHandle 是一个用于绘图的对象但是我们并不能直接使用这个参数。

简要解析一下绘图对象

前往CDCHandle的定义
CDCHandle
可以看到他是一个CDCT类不同泛型参数的别名,与之对应的还有一个CDC
接下来前去看看CDCT的泛型参数发挥了什么作用
CDCT的定义
通过CDCT的定义 可以看出来 泛型参数 t_bManaged声明了一个CDCT对象所持有的HDC对象是否由自己管理,进而在CDCT销毁时销毁HDC
此处的HDC是用于绘图的由WinApi定义的句柄。
简单来说这样的做法是为了绘图器持有的句柄可以被以值传递的方式传递,不影响句柄。但是最终句柄只会在创建者销毁时销毁
当然就算不理解也没关系。知道接下来应该这么做就行了。
使用CPrintDC类,它在ATL中被定义用来从窗口句柄获取HDC句柄
看一看CPrintDC的定义
CPrintDC

使用CPrintDC
绘图
HBRUSH是WinApi提供的画刷工具 CreateSolidBrush是创建单色画刷,这里我们创建一个保护色画刷
GetClientRect是获取当前控件相对于自身的位置。也就是自己的大小。传递一个CRect的地址会对它赋值。
FillRect则是对对应的区域指定画刷。也就是绘图操作。
绘制窗口背景色

动态绘图

如果你接触过其他平台的界面API应该可以直接想到下面这个注意点:
绘图操作仅可以由系统调用绘图方法是一个特定过程执行的方法。不能主动调用
比如如下操作
通过消息添加一个鼠标点击的响应事件,当鼠标左键在窗口内点击的时候会调用OnLbuttonDown
在此处添加想要做的操作
更新界面

Invalidate() 方法是可以用来更新控件的方法,他会使得该控件失效并由系统在空闲的时间更新。
UpdateWindow() 方法可以让失效的控件立即刷新,也就会重新绘制。如果不需要立即刷新 可以不用添加。
可以看到,不论是直接使用画刷 还是在外部调用OnPaint方法 都没有让界面颜色发生改变。
生效时机
界面变为蓝色 实际上是系统自动调用了OnPaint方法之后生效的。
接下来我用这段代码演示一下这个过程
换个颜色
黑色方块缺失是因为GIF录制会有丢帧
添加的代码在鼠标点击时先绘制背景然后在鼠标周围绘制一个黑点。在鼠标抬起时将黑点的位置取消,然后再次更新绘图。
然后补充一点功能
画笔CPen和文字绘制
画笔和文字

	void printLine( CDCHandle dc) {
		CPen pen;
		pen.CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
		dc.SelectPen(pen.m_hPen);
		CPoint start;
		start.x = 10;
		start.y = 10;
		dc.MoveTo(start);
		CPoint dest;
		dest.x = mouseClickLocation.left;
		dest.y = mouseClickLocation.top;
		if (dest.x != 0)
		{
			dc.LineTo(dest);
		}
	}

	void printText(CDCHandle dc) {
		CString text;
		CRect textLocation;
		text = "hello world";
		dc.SetTextColor(RGB(0, 0, 0));
		dc.ExtTextOutA(mouseClickLocation.left, mouseClickLocation.top, ETO_OPAQUE, NULL, text, text.GetLength(), NULL);
	}

通常来说如果你对 CPringDC的调用是一次独立绘图的话
你应该在绘图前使用SaveDC保存并在最后对它调用
RestoreDC(-1) 方法以恢复你使用之前的状态
保存和回复DC
也许你注意到了 这里我已经将OnPaint方法替换为了DoPaint方法,这牵扯到接下来要补充的一点

双缓冲

在进行绘图时 如果你反复重绘,会因为绘图事会绘制一部分 显示一部分,重绘过快就会在上一次绘制完成前就进行下一次绘制,进而导致闪烁。
闪烁现象
此时就需要用到 CDoubleBufferImpl,它被设计成类一个基类,相当于扩展,此时产生多继承。
它的实现
CDoubleBufferImpl
CDoubleBufferImpl自身接收了会调用绘图的消息。然后提供了一个接口 DoPaint
因此要这样使用
1.添加类继承
继承
2.添加消息链接
添加消息链接
CHAIN_MSG_MAP可以将对应类中的消息链接到当前类中
由于CDoubleBufferImpl已经实现了MSG_WM_PAINT(OnPaint),因此需要注释掉。
3.实现DoPaint方法
也就是将你本来需要绘图的内容都放到这里。DC已经在CDoubleBufferImpl中被获取并传递到DoPaint了,因此直接使用就行了。
DoPaint
看看效果
添加CDoubleBufferImpl后
在鼠标移动事件中如果进行复杂绘图有可能导致绘图错误,这可能是由于鼠标回报率(中高端鼠标可以达到1000HZ)过高对绘图的调用超过GPU的渲染能力导致的。这种情况下需要主动降低绘图频率,如限制在60帧内之类的方案,同时对独立窗口是分别独立绘制的,所以应该尽量避免大量自定义绘图窗口同时重绘
关于绘图 这里就不过多延申了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WTL 具有两面性,确实是这样的。它没有MFC的界面(GUI)类库那样功能强大,但是能够生成很小的可执行文件。如果你象我一样使用MFC进行界面编程,你会觉得MFC提供的界面控件封装使用起来非常舒服,更不用说MFC内置的消息处理机制。当然,如果你也象我一样不希望自己的程序仅仅因为使用了MFC的框架就增加几百K的大小的话,WTL就是你的选择。当然,我们还要克服一些障碍: ATL样式的模板类初看起来有点怪异 没有类向导的支持,所以要手工处理所有的消息映射。 MSDN没有正式的文档支持,你需要到处去收集有关的文档,甚至是查看WTL的源代码。 买不到参考书籍 没有微软的官方支持 ATL/WTL窗口与MFC的窗口有很大的不同,你所了解的有关MFC的知识并不全部适用与WTL。 从另一方面讲,WTL也有它自身的优势: 不需要学习或掌握复杂的文档/视图框架。 具有MFC的基本的界面特色,比如DDX/DDV和命令状态的自动更新功能(译者加:比如菜单的Check标记和Enable标记)。 增强了一些MFC的特性(比如更加易用的分隔窗口)。 可生成比静态链接的MFC程序更小的可执行文件(译者加:WTL的所有源代码都是静态链接到你的程序中的)。 你可以修正自己使用的WTL中的错误(BUG)而不会影响其他的应用程序(相比之下,如果你修正了有BUG的MFC/CRT动态库就可能会引起其它应用程序的崩溃。 如果你仍然需要使用MFC,MFC的窗口和ATL/WTL窗口可以“和平共处”。(例如我工作中的一个原型就使用了了MFC的CFrameWnd,并在其内包含了WTL的CSplitterWindow,在CSplitterWindow中又使用了MFC的CDialogs -- 我并不是为了炫耀什么,只是修改了MFC的代码使之能够使用WTL的分割窗口,它比MFC的分割窗口好的多)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值