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

由于是很少使用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,在该函数中设置控件的文本颜色,背景透明,并返回一个空画刷,以实现透明效果。


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

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

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


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

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


先获取控件的子区域矩形,然后调用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。


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


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


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


2.2 对CLabel开源类的改造

 

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

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

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


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


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


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


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

2.3 对CTransparentStatic开源类的使用

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


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


我在CSDN上传了对于这两个改造类的演示的DEMO,最新版本有些改动,跟这篇文档有些不同,不过并不影响

感兴趣的话,欢迎下载,免费的

需要说明的是这个DEMO在Debug模式下会出现一个Attach断言错误,这是我自己造成的,由于不能删除上传的资源,所以我单独写了一篇勘误博客文章

这是我的小白用MFC的工作日志的一部分,感兴趣的话,我在豆丁有个word的完整版,欢迎下载,免费的

由于是第一次在csdn写博客,如有错别字,版式太乱,请多包涵!!






  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值