深度探索WM_PAINT消息

转载 2011年01月20日 15:43:00

【引言】:这是以前在作VC/EVC开发时候遇到的一个郁闷的问题的思考,刚好最近在VC#开发时候遇到了类似的问题,因此就总结出来,期望能够给遇到同样问题的开发者一些帮助和提示。

 

问题缘起

       半年前,在作Mobile GPS项目(移动手持设备上的GPS/GIS项目)的时候,为了实现PDA上的地图下载功能,我们将GIS地图数据转化为XML文件,在经过相关技术的优化终于做到了将XML的地图解析并绘制到PDA屏幕上。但是问题出现了:当在响应一个绘制地图的菜单(Button)项目的时候,地图绘制出来了,但是当有新窗体在上面经过(模拟器上运行)或者是触摸笔擦写PDA屏幕的时候,画好的地图被擦写掉了!

       很显然地,为了调试这个Bug,我在VC下面去重复这样的实现(当然是简单的模拟),问题还是存在。但是将直接绘图操作放在OnPaint()中则不会出现这样的问题,但是这不能解决应用问题(图像应该是一步步绘制的,并且在不同的时候有不同的显示)。

原因揭密

       这个问题的原因是WindowsWM_PAINT消息的处理,WM_PAINT消息的相关知识这里不罗嗦(可以参考相关资料),需要强调和说明的是:

1)  应用程序第一次运行的时候WM_PAINT会被自动调用,也就是说你在OnPaint()中实现的绘图操作会被调用;

2)  当窗体的大小被调整(例如最小化、拉伸等),或者被遮挡后又显示出来(问题中的情况)的时候会调用OnPaint()函数。

于是上面的问题的原因就是:当窗体被遮挡后,WM_PAINT消息使得窗体重画,但是

我们的绘画逻辑不是放在OnPaint()中实现(是在菜单项或者Button的响应里面实现的),因此调用后不能获得对应的绘图显示。

解决方案

       问题出现了,项目还要做,客户的需求还是要满足,因此就得想办法解决了。在弄懂了整个问题的原因后,解决方案也就慢慢又了眉目:既然是因为系统调用OnPaint引起的,那就在OnPaint和绘图逻辑间做一个权衡:

1)  将绘图过程放在OnPaint中实现,当然可以是在另外函数中实现,在这里调用即可;

2)  菜单(Button)只控制绘图逻辑:设置一个BOOL变量m_bDraw来标志是否进行绘图,初次可以设计为FALSE。菜单和Button的响应逻辑中只是修改这个BOOL变量,例如标志为TRUE,提示绘制图形;

这个问题看上去已经解决了,但是这样实现后,发现当菜单项(Button)点击后,图像

没有绘制出来。一思量,确实应该是这样:因为菜单项(Button)逻辑只是修改了m_bDraw的值,并没有去显示调用OnPaint消息。于是你会发现,当你有上面问题中的操作(例如最小化、遮挡等)时候,图像如期而至(因为这会出发WM_PAINT消息)。

       知道原因,解决方案就出来了:在菜单项(Button)响应逻辑中除了改变m_bDraw的状态,还要发送WM_PAINT消息,以调用OnPaint函数。于是,只需要在2)中响应逻辑中除了改变BOOL变量值,再加上出发WM_PAINT消息的操作,你可以有以下3种选择:

1)  直接发送WM_PAINT消息,用PostMessage()SendMessage()函数发送WM_PAINT消息。使用以上两函数发送WM_PAINT消息,能将WM_PAINT消息发送到WINDOWS程序消息队列中,当WINDOWSWM_PAINT消息发送给具体的消息处理函数时,如果窗口的无效区域为空则WINDOWS将不理睬该消息。若存在无效区域,则调用窗口处理函数处理。要注意的这里需要存在无效区域,因此要调用2)中的函数使得窗体(或者部分)无效,其处理过程与2)相同,将WM_PAINT消息送入消息处理队列。与3)不同的是WM_PAINT并不立即处理;

2)  调用相应的API实现WM_PAINT消息的发送:Invalidate()InvalidateRect() InvalidateRgn():以上函数将窗口的特定区域标定为无效,当WINDOWS检测到窗口中存在无效区域时将向消息队列发送WM_PAINT 消息。我当时用的就是Invalidate()函数;

3)  UpdateWindow():该函数调用后WINDOWS将向窗口发送一个非队列化的WM_PAINT消息,它不经过消息循环而直接发送给了窗口消息处理函数。如果窗口无效区域不存在,WINDOWS将不理睬该消息。注意这里因为要使得窗口无效区不存在,因此还是调用Invalidate()InvalidateRect() InvalidateRgn()函数,和2)中不同的是这里的WM_PAINT消息会被立即处理,而2)中是加入消息处理队列。

简单起见,你可以使用2)中方案进行问题解决。

问题扩展

上面提到的都是在VC6.0EVC 3.0下面的问题和解决方案,最近在做VC#开发的时

候,也遇到同样的问题,其原因和上面当然是一样的。解决方案也类似,只不过VCEVC)中绘图使用CDC(或其子类CPaintDCCClientDC等)而VC#使用Graphics,解决方案也差不多,不同的是:显示发送WM_PAINT消息的方法略有不同:在上面的发送WM_PAINT消息的方案2)中,你可以是调用以下的API之一:

1)  Invalidate():一共有7个重载的函数体,可以根据实际情况调用(相当于是上面的3API功能);

2)  Update():注意这个操作和解决方案中的3)相似,因此必须还要调用1)中的Invalidate,与1)不同的是这里WM_PAINT消息理解被触发;

3)  Refresh():这个操作相当于Invalidatetrue+ Update(),因此也可以取得同样的效果。

需要说明的是:当你在VC#Windows Form下面测试上面的问题时候,你会发现另外

一个郁闷的事情:当窗体大小被调整的时候,看上去似乎图像被重画了N次,但是每次都不彻底。问题的原因是:当窗体被扩大时,Windows只绘制那些新近显露出来的矩形区域,而假定原有的矩形区域无需重绘。这个问题可以通过设置Form风格来处理,在Form的构造函数里InitializeComponent()后面加上改变更个语句:SetStyle(ControlStyles.ResizeRedraw,true);一切就OK了。当然如果你要问为什么默认的不是这种风格,那是因为这种做法的效率很低下(想想就知道了)的原因。

几点说明

       本文在VC 6.0VS 2003中均进行了测试,你可以通过以下方式构造测试程序:

1)  VC 6.0下,你可以新建一个基于Dialog或者Single DocumentMFC程序,在前者你可以通过响应一个Button,后者通过一个简单的菜单响应,绘制一个椭圆,看实际的问题和解决方案的可行性;

2)  VS 2003中,新建一个VC#Windows应用程序即可,同样通过响应一个Button绘制椭圆,以获得1)中效果。

 

本文转自:http://www.cnblogs.com/k-eckel/articles/188463.html

Win32 SDK基础(12)—— WM_PAINT消息的处理

一、引言         在计算机中,屏幕上显示的一切东西几乎都是绘制的,包括窗口、对话框、图片、以及一切文字,而WM_PAINT消息就是在绘制这些对象时,系统触发的消息。我们在计算机中的每一个操作,...
  • lzhui1987
  • lzhui1987
  • 2017年04月13日 14:46
  • 1897

窗口刷新问题(WM_PAINT)

在Windows API编程中,WM_PAINT是Windows窗口的一个重要消息,应用程序就是通过响应这个消息来完成窗口的绘制。   The WM_PAINT message is generat...
  • heary29
  • heary29
  • 2014年12月20日 10:53
  • 480

系统何时发送WM_PAINT消息

系统何时发送WM_PAINT消息?   系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些...
  • vivian_king
  • vivian_king
  • 2015年04月14日 11:08
  • 880

深度分析WM_PAINT和WM_ERASEBKGND消息

做windows开发这么久了,一直以来对WM_PAINT和WM_ERASEBKGND消息总是感觉理解的不准确,每次要自绘一个窗口都因为知其然不知其所以然,偶然发现一篇文章,详细透彻地分了这个两个消息的...
  • analogous_love
  • analogous_love
  • 2015年11月25日 17:10
  • 708

深度探索c++对象模型

所谓知己知彼,百战不殆。只有深入了解了c++对象的内存布局,我们才能更熟练运用c++这门语言。 运行环境vs2013一单继承和多继承的结合class C { public: C() :c(1...
  • db199410
  • db199410
  • 2016年06月16日 17:40
  • 375

Windows GUI WM_PAINT消息一直发送的问题

while(msg.message != WM_QUIT) { // If there are Window messages then process them. if(PeekMe...
  • wuanshi5
  • wuanshi5
  • 2013年07月09日 09:28
  • 857

深度探索c++对象模型(一)_关于对象

原博客地址:http://www.roading.org//develop/cpp/c对象面面观.html 学习C++应该看过不少关于C与C++的口水贴,以及关于各种对比C与C++效率比较的...
  • A_IIIIIIIII_A
  • A_IIIIIIIII_A
  • 2016年01月04日 11:31
  • 512

UCGUI几个基本消息总结

转自:  UCGUI是采用消息驱动的,它专门有一套对外收集消息的接口,要使用UCGUI,必须对它的消息驱动机制有所了解。 UCGUI的一些基本消息列举如下: -WM_CREATE——窗体创建消息...
  • zcx515545
  • zcx515545
  • 2014年08月05日 15:36
  • 860

【深度探索STL】空间配置器(一) 构造和析构

以STL运用的角度而言,空间配置器总是隐藏在一切组件的背后,但就STL 的实现角度而言,我们需要了解空间配置器,因为整个STL 的操作对象(所有数值,“value”语意)都存放在容器之内,而容器一定需...
  • yeswenqian
  • yeswenqian
  • 2014年02月20日 14:59
  • 1631

《深度探索C++对象模型》读书笔记——关于对象【for_wind】

//整理之,分享之,欢迎指正。for_wind 1、C与C++的区别:       概括来说,C程序中程序性地使用全局数据[注1]。而C++采用ADT(abstract data tpye)或...
  • for_wind
  • for_wind
  • 2014年03月28日 14:08
  • 1383
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章: 深度探索WM_PAINT消息
举报原因:
原因补充:

(最多只允许输入30个字)