MFC双缓冲和裁剪问题导致闪烁
问题描述:
应用场景:在对话框中,自定义一个MFC图形控件(为了描述方便,暂定为HSPaintControl),控件覆盖整个对话框的客户区,属于最底层的控件,在这之上放置了很多其他的小图形控件。
问题:更具业务需要,HSPaintControl需要高频率(大概是60FPS)的刷新,为了解决闪烁问题,使用了双缓冲技术。运行时,HSPaintControl不断刷新,其上层的其他小控件也在刷新,HSPaintControl控件不会闪烁,但上层小控件界面一直在闪。
问题的原因:
HSPaintControl使用双缓冲刷新时,覆盖了整个背景,上层小控件界面显示,也会执行一次刷新,从而导致一个地方界面刷新了两次,就导致闪烁问题。
界面不闪烁的绘制原则:
确保界面每个像素只刷新一次。
解决办法:
1. 能解决该问题最笨的方式是完全自己控制,在HSPaintControl刷新时,不刷新上层控件所在区域。HSPaintControl刷新时,获取上层的所有子控件,计算区域,然后一个一个排除。这个方法有两个比较麻烦的问题:第一是如何获取在上层存在遮盖的控件;第二是要计算排除所有这些控件和HSPaintControl控件的叠加区域。最开始的时候,上层只有一个控件,而且是完全放在上上面的,可以硬编码的方式解决上面两个问题(可以自己计算排除区域,再使用BitBlt函数一块一块的拷贝,也可以借助于函数ExcludeClipRect,直接排除掉,当然使用已经有的函数,是比较方便的)。但是后来小控件越来越多,就很麻烦了。
2. 上层小控件很多的情况下,第一个方法就搞不定了,考虑到MFC自己的控件叠加刷新时,都不存在闪烁的问题,那么肯定在内部实现了裁剪,经寻找果然存在该机制,通过设置自定义控件的样式即可搞定,增加样式WS_CLIPSIBLINGS(不明白的请google一下)。自定义控件是基于CStatic做的,在实现时,重载函数PreSubclassWindow,添加上对应的样式设置语句:
// TODO: 在此添加专用代码和/或调用基类 DWORD dwStyle = GetStyle(); SetWindowLong(GetSafeHwnd(),GWL_STYLE,dwStyle | WS_CHILD | WS_CLIPSIBLINGS | SS_OWNERDRAW ); CStatic::PreSubclassWindow();
用系统自带的机制,确实就方便多了。如果自己要实现图形系统,那么相应的裁剪就还得自己来做了。
值得注意的是ExcludeClipRect函数和样式WS_CLIPSIBLINGS对BitBlt函数都会造成影响。