window窗口闪烁小结

首先,这两天的论坛上,很多人又提“防闪烁”的问题;然后是读了这位浅墨老兄的这篇博客,有感而发。

(这位老兄貌似也是转发的,浅墨兄文笔很好,超赞)http://blog.csdn.net/zhmxy555/article/details/7436397


闪烁的问题有时候很难,涉及的东西太多;有时候很简单,处理WM_ERASEBKGND消息即可;有时候需要双缓冲;有时候还要改窗口样式。。。

其实要真正处理好,需要知道原理、原因两个“原”就可以对症下药了。如果觉得简单,就跳过好了,但很可能是你遇到的情况不够复杂。

(以下以普通的windows窗口为例)


第一,原理。

从人的视觉角度来讲,如果两张图片的视觉效果反差极大,当这两张图片切换的时候,人眼会有刺眼和难受的感觉,产生闪烁。

所以说,你给用户展示的东西本身有极大反差的时候,是用任何方法都无法消除闪烁的。

再看普通的windows窗口,它的绘制过程是先画白色或淡灰色背景(View或Dialog,默认处理WM_ERASEBKGND),

然后再画前景(图像或子窗口和控件绘制,Draw/Paint)。图像或子控件的位置由背景色变成前景色,人眼会看到这个过程,从而产生闪烁。

所以,普通情况下,单单用双缓冲是无法避免闪烁的


第二,原因。原因肯定是在闪烁的位置,有视觉上的切换反差,消除即可。那么我们必须要做到的,就是要找对地方,就是哪个地方导致了闪烁。

有的窗口很简单,就两层,窗口加子控件;有的就比较复杂了(比如我遇到的,视图View,上面嵌一层TabView,再上面分左右两个View,

左右两个View上又布满子控件(甚至大于255个),控件还可能要切换(隐藏、显示、折叠),还包含GroupBox(这个提出来,是因为确实比较特殊))。

这种情况包含了4层窗口,还要动态变化。

无论多少层的,都一个办法,从下到上,一层一层的解决。


第三,找对地方之后,我们就要下猛药了。(确实因为情况比较多,这里就举一些常见的情况,且情况和解决方法不是一对一的。)

1.完全被其他窗口覆盖的底层窗口,这种情况最简单了,可以处理WM_ERASEBKGND消息,直接返回TRUE(反正都会被覆盖掉);

如果上面有子窗口覆盖,可以增加窗口样式WS_CLIPCHILDREN(视情况加WS_CLIPSIBLINGS,以下再提到则省略)。

2.部分被其他窗口覆盖的父窗口,比如FormView,可以增加窗口样式WS_CLIPCHILDREN。

3.由于一系列MoveWindow子窗口引起的闪烁,可以用BeginDeferWindowPos, DeferWindowPos,EndDeferWindowPos系列函数。

4.如果是绘图窗口,有很多图形元素(比如CAD等,可能一屏显示成千上万图元),就需要用内存DC(也就是双缓存)。因为绘制周期要很长时间,

中间会输出到屏幕上,造成图像一点一点出来的动画的感觉)。当然,此时的优化也是必须的(不出现在屏幕上的图元不绘制)。

5.对于有背景图或需要重绘背景图的情况,要处理WM_ERASEBKGND+双缓存。

6.对于内嵌的IE窗口,我使用处理WM_ERASEBKGND的方法(::SetWindowLong(..., GWL_WNDPROC...),不过要找3层窗口),浅墨兄文中提到的还未尝试用过。

7.对于隐藏、显示、折叠的窗口,不多的话基本不用特殊处理,但是有时候会有副作用(花屏和错位,下面会提到)

8.绘制部分:带参数的InvalidRect,只绘制脏数据区(对于绘图程序可能比较常用)

9.SetRedraw/RedrawWindow/LockWindowUpdate至于你用不用,反正我不常用,所以了解不多。

另外,深入要了解LockWindowUpdate可以移步这里http://blog.csdn.net/BalonFan/article/details/1594475

要注意的是,还有很多情况没有列举出来,而且很多方法要综合运用。


第四,副作用。

1.花屏,该画的背景没画。最常见如下情况:窗口里面有GroupBox(上面提到过),GroupBox里有控件。当窗口样式包含WS_CLIPCHILDREN的时候,

绘制背景的时候会把GroupBox剪切的,系统会指望子控件自己绘背景;而恰恰GroupBox是个极特殊的控件,其绘制的时候,只画边框部分,中心区透明,让父窗口画。

这样你推我我推你,没人画,就花屏了。解决方法是SubClass GroupBox控件绘制背景,而有背景图的时候,需要掌握好尺寸;或者不用控件,在GroupBox位置绘制背景;或者让父窗口画(父窗口窗口样式不包含WS_CLIPCHILDREN,绘制背景的时候,把其他子控件剪切)。

2.错位,一般用在滚动的视图里(比如模拟折叠效果),都是座标没转换好,细心点就没问题,注意ScrollSize,避免最下面空间不够或大片空白。

3.静态标签控件浅灰色底纹,如果不想SubClass,直接处理WM_CTRLCOLOR,大部分地球人都知道。


错误之处,欢迎各位网友给予指正。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章