CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

时间:2024-04-05 15:04:28

由于是很少使用VS2008的MFC,遇到了很多麻烦,其实都是很初级的;但是我还是想做点记录,以示对自己工作的鼓励,同时也是留个纪念,因为随着微软政策的变化,以后MFC的程序将不会是趋势了,而我也有意从事Android平台的开发,所以不太可能做MFC程序了。


需求是这样的,我用一个CStatic控件显示远程发送来的文本信息,由于是不断获取,所以会不断刷新,程序主界面是对话框,对话框的背景是一张以蓝色为主色调的BMP图片,我希望CStatic控件能够透明显示文本,而不是一个白色不透明方框。

    在解决的过程中,出现了,背景透明后,有文本重叠问题;解决文本重叠问题,却带出了界面刷新闪烁问题。因为绘图是需要进行一系列图像处理的,一擦一绘会造成图像颜色反差,WM_PAINT消息的频繁响应,势必造成重绘过于频繁,必然形成严重的闪烁。

    因此我对MFC窗口绘图框架,做了简单了解,对窗口绘制和重新绘制过程中的OnPaint,OnEraseBkgnd,OnCtrColor,InvalidateRect,UpdateWindow有了一个大致上的认识。网上有很多资料,这里不介绍了。

    对于背景重绘一般是把实现代码放在OnEraseBkgnd或者OnPaint中来实现的。如果放在OnPaint中,需要同时重载OnEraseBkgnd函数并且不添加任何代码直接return TRUE;

1.使用WM_CTLCOLOR消息

首先我想到的是MFC的控件应该有一个直接设置背景透明的API,很遗憾CStatic在VS2008的MFC的版本没提供这样的API。

所以我选择了在网上比较多的一种做法,在主对话框使用WM_CTLCOLOR消息,并重载消息响应函数OnCtlColor,在该函数中设置控件的文本颜色,背景透明,并返回一个空画刷,以实现透明效果。

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

结果可想而知,静态和动态文本都实现了透明,但动态刷新文本时出现了文本重叠,原因是OnCtlColor中返回了空画刷,相当于没有刷新,直接重绘的。

为了解决刷新的问题,我使用了,如下两种方式:

1.  将控件隐藏然后再显示,以达到强制重绘的目的,实现代码如下图:

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

m_ticket_type.是一个CStatic的对象,关联了一个控件IDC_TICKET_TYPE,结果是,背景是透明的没问题,文本也没有重叠了,但我始终觉得这种方式比较消耗资源,而且界面出现了严重的闪烁。

1.  调用相关系统API,强制刷新控件区域,实现代码如下图:

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

先获取控件的子区域矩形,然后调用InvalidateRect函数强制刷新,该函数会抛出WM_PAINT消息,由OnPaint中的代码实现刷新重绘。这段代码是在主对话框中完成的,即一般大家所说的,通过父窗口强制刷新控件区域。同样的,这个方法也解决了背景透明问题,文本重叠问题,但是还是有严重的闪烁,而且跟区域大小没有关系,所以我怀疑跟设备性能关系不大,因此放弃了在此基础上添加双缓冲方式绘图的想法。而且这种在父窗口通过响应系统消息,刷新子控件的做法,也不符合面向对象封装的特性,因此我放弃了这种方式。

2.子类化CStatic

    要解决上述问题,同时达到面向对象要求,只有通过继承CStatic类,并在子类中实现背景透明,字体设置,文本颜色设置等功能。这方面可以自己写,也可以使用开源代码,在www.codeproject.com网站上提供了很多功能比较完善,封装也比较好的的开源类,可以免费使用,不需要下载积分,可以使用国内的网易邮箱注册账号。需要注意的是,这些类往往只是提供思路的,要实现自己特殊功能,还得自己添加成员函数,不过人家已经搭好框架了,添加个功能就方便多了。

    需要强调的是MFC在子类中添加WM_CTLCOLOR消息处理函数OnCtrColor是不会被响应的,MFC4.0以后提供了一个消息反射的机制,在VS2008中添加消息时消息名称前面包含“=”(等号)的即是支持消息反射的,=WN_CTLCOLOR消息,添加到代码中是ON_WM_CTLCOLOR_REFLECT(),响应函数是CtlColor。

2.1 对CLabel开源类的使用

    该类是一个封装的挺漂亮的一个类,将其添加到我的工程,可以直接声明对象使用,各种功能也仅仅是调用成员函数,很方便。该类是在OnPaint中实现重绘的,使用了双缓冲,如下所示,当控件设置为透明时,内存DC是直接获取的OnPaint中的dc。

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

不过我不明白的是,pDCMem通过DrawText设置文本内容后,当控件设置为背景透明时,并没有使用BitBlt将内存DC拷贝绘制的过程,但在界面上我仍然能够看到新设置的文本,也许是我对代码的理解还不够。

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

如前所述OnEraseBkgnd函数在OnPaint重绘背景的情况下,直接返回TRUE,原因是OnPaint实际上回默认调用OnEraseBkgnd的,如果不重载OnEraseBkgnd,该函数是默认绘制白色背景的矩形框,这将造成明显的闪烁。

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

通过下面的方式能够实现透明,意思是设置“窗口”为透明

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

2.2 对CLabel开源类的改造

 

尽管该类封装的很漂亮,功能也比较完善;然而,我经过多次测试发现,当需要动态刷新CStatic控件是,仍然有很明显的闪烁。我分析还是由于多次刷新的问题,SetTransparent也许只是设置窗口的一个状态,必须返回一个空画刷,才能避免绘制一个白色框,造成闪烁。下面采用消息反射,重载ON_WM_CTLCOLOR_REFLECT消息响应函数CtlColor

添加该消息响应的方法如下:

    1.在类视图中选中该类名

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

2.鼠标右击选择属性打开。

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

3.在弹出的“属性”窗口中点击红色方框按钮,选择=WM_CTLCOLOR消息,在蓝色方框的下拉菜单中选择添加CtlColor

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

4.在CtlColor消息响应函数中添加如下代码,并注释return NULL

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

改造过后的CLabel对象能够很好的支持控件背景透明,文本重叠,刷新闪烁问题;但是该方法我并没有做性能测试,反正目标机器完全不需要考虑这个问题。

2.3 对CTransparentStatic开源类的使用

该类也是在CodeProject上分享的一个继承类,它不需要改造,能够完美解决我的问题,它采用在OnEraseBkgnd里重绘界面的方式,使用双缓冲绘图技术,同时重载CtlColor函数实现透明

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题

遗憾的是,该类只提供透明等问题的解决方案,功能太少,至少连字体和文本颜色设置都没有,因此我为它添加了三个成员函数以实现我的需求,直接使用即可,很简单就不注释了。

CStatic子类化解决背景透明,文本重叠,刷新闪烁问题