首先说说WM_PAINT 这个重要的消息:

  The WM_PAINT message is generated by the system and should not be sent by an application.The system sends this message when there are no other messages in the application's message queue

  也就是说WM_PAINT消息是由系统产生,非要等应用程序的消息队列为空时才发送WM_PAINT消息,并且 该消息不应该被程序(自己写代码用SendMessage)来发送。

  当调用UpdateWindow函数,或者是Window检测到 窗口被覆盖的地方需要恢复的时候,比如,第一次创建窗口,改变了窗口的大小,最大化,最小化等等(其实这些事件发生时会调用UpdateWindow函 数,由该函数发送WM_PAINT消息),它会向用户程序发送一个WM_PAINT消息。窗口过程收到WM_PAINT消息后,并不代表整个客户区都需要 被刷新,有可能客户区被覆盖的区域只有一小块,这个区域叫做“无效区域”,程序只需要更新这个区域。与WM_TIMER消息类似,WM_PAINT消息也 是一个低级别的消息,虽然它不会像WM_TIMER消息一样被丢弃,但Windows总是在消息循环空的时候才把WM_PAINT放入其中。

  无效区域的坐标并不附带在WM_PAINT消息的参数中,在程序中有其他方法可以获取。WM_PAINT消息只 是通知程序有个区域需要更新而已,所以Windows也不会同时将两条WM_PAINT消息放入消息循环中。当Windows要放入一条WM_PAINT 消息的时候,如果发现已经存在一个无效区域了,那么它只需要把新旧两个无效区域合并计算出一个无效区域就可以了,消息循环中还是只需要一条 WM_PAINT消息。

  实际上,Windows为每个窗口维护一个“绘图信息结构”,无效区域的坐标就在其中,每当消息循环空的时候, 如果Windows发现存在一个无效区域,就会放入一个WM_PAINT消息。那么“绘图信息结构”怎么获取呢?BeginPaint函数的第二个参数就 是一个绘图信息结构的缓冲区地址,windows会在这里返回绘图信息结构,结构中包含了无效区域的位置和大小,绘图信息结构的定义如下:

typedef struct tagPAINTSTRUCT { // ps 
HDC   hdc; 
BOOL fErase; 
RECT rcPaint; 
BOOL fRestore; 
BOOL fIncUpdate; 
BYTE rgbReserved[32]; 
} PAINTSTRUCT; 

  其中hdc字段是窗口的设备环境句柄,rcPaint字段是一个RECT结构,它指定了无效区域矩形的对角顶点 (如果开始有一个((0,0),(40,40)),现在又来一个((20,20),(60,30)),那么拼接后就是((0,0), (60,40))),fErase字段如果为非零值,表示Windows在发送WM_PAINT消息前已经使用背景色擦除了无效区域,后面3个字段是 Windows内部使用的,应用程序不必去理会他们。

  在某些情况下,显示区域的一部分被临时覆盖,Windows试图 保存一个显示区域,并在以后恢复它,但这不一定能成功。Windows可能发送WM_PAINT消息:Windows 擦除覆盖了部分窗口的对话框或消息框;菜单下拉出来,然后被释放;显示工具提示消息。

  在某些情况下,Windows总是一定保存它所覆盖的显示区域,然后恢复它。这些情况是:鼠标光标穿越显示区域;图标拖过显示区域。

  有时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过 InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。

  系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发 送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽 可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区 域就会被累加起来,然后在一个WM_PAINT消息中一次得到 更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无 效,依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以 在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送 WM_PAINT消息而不管Update Region是否为空等。

  BeginPaint

  今天在处理WM_PAINT消息时产生了一个低级的错误,并搞的我花了快一个小时才找到原因。我在处理消息时, 没有使用BeginPaint和EndPaint这对函数,结果我其余的消息弹不出来,窗口拖动时,不停闪烁(其实那就是重绘)。后来还是在MSDN上找 到了答案,现将原话贴出来。(在MSDN的The WM_PAINT Message标题中)

  BeginPaint sets the update region of a window to NULL. This clears the region, preventing it fromgenerating subsequent WM_PAINT messages. If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty. In all cases, an application must clear the update region before returning from the WM_PAINT message. 
BeginPaint函数的作用就是将窗口需要重绘的区域设置为空(也就是Update Region置空)。在正常情况下,我们接收到了WM_PAINT消息后,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了)。而当你响应这个消息的时候又不调用BeginPaint来清空,窗口的 Update Region就一直是非空的,系统就会一直发送WM_PAINT消息。这样就形成了一个处理WM_PAINT消息的死循环。

  BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写 BeginPaint会怎样?程序会像进入了一个死循环一样达到惊人的CPU占用率,你会发现程序总在处理一个接 一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息时,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了),BeginPaint的一个作用就是把该Update Region置为空,这样如果不调用BeginPaint,窗口的Update Region就一直不为空,如前所述,系统就会一直发送WM_PAINT消息。

  BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时,BeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其返回信息里有一个标志表明窗口背景 是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时,可以设置该区域是否需要被擦除背景,这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。

  当然关于 WM_PAINT消息还有很多的知识需要学习。另外要注意的一点是,BeginPaint只能在WM_PAINT处理函数中使用,并且在调用了 BeginPaint函数后,不要忘记了调用EndPaint函数,他们可是一对的。

  重画 函数 InvalidateRect,Invalidate,UpdateWindow, RedrawWindow

  InvalidateRect(部分区域) 和Invalidate(整个窗口) 仅仅是用来设置无效区域,但是并不重绘窗口。

  UpdateWindow 检查窗口有无无效区域,如果有,则立即发送一个WM_PAINT 消息给窗口并立即重画。 

  RedrawWindow相当于先调用InvalidateRect,紧接着又调用UpdateWindow, 此外RedrawWindow还提供了一些前两者没法做到的功能。

如果不调用 InvalidateRect就调用 UpdateWindow,那么UpdateWindow什么都不做,因为没有无效区域。如果调用 InvalidateRect 后不调用UpdateWindow,则系统会自动在窗口消息队列为空的时候,系统自动发送一WM_PAINT消息。

【转】VC窗口刷新InvalidateRect和UpdateWindow RedrawWindow相关推荐

  1. VC窗口刷新InvalidateRect和…

    学习了 原文地址:VC窗口刷新InvalidateRect和UpdateWindow 作者:玩世不恭 首先说说WM_PAINT这个重要的消息: The WM_PAINT message is gene ...

  2. VC++ 关于窗口刷新的几个函数InvalidateRect、UpdateWindow、RedrawWindow

    在前面写了那么多自绘控件,有必要对这几个接口做下声明解释: 这几个接口都是对执行窗体重绘的消息,那么什么时候才会发生重绘窗口的消息?答案是:当窗体需要更新内容或是重绘外观背景的时候,应用程序通常会发送 ...

  3. 窗口刷新 Invalidate UpdateWindow RedrawWindow

    窗口刷新的几个函数:InvalidateUpdateWindowRedrawWindow 一:什么时候才会发生重绘窗口的消息?        当需要更新或重新绘制窗口的外观时,应用程序就会发送WM_P ...

  4. InvalidateRect()、UpdateWindow()、RedrawWindow()区别

    先看一段移动图片的程序: 绘图工作都在ReDraw()中 响应MouseMove  改变图片的位置,鼠标移动后需要重绘窗口 实验一: ReDraw()在MouseMove函数中调用 [html] vi ...

  5. Windows窗口刷新机制详解

    1.Windows的窗口刷新管理 窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个z-order序列,记录着当前窗口从屏幕底部(假象的从屏幕到眼睛的方向),到屏幕最高层的一个窗口句柄的排序 ...

  6. 关闭子窗口刷新父窗口

    以下这段代码是为了弹出一个窗口,window.showModalDialog这个是弹出窗体的地方,当执行到的时候它后面的代码就不执行了,当把弹出的窗体关闭将会执行后面的location.reload( ...

  7. layui table reload post请求_如何实现在Layui框架中完成父窗口刷新(更新)

    最近有个项目使用Layui框架进行后端系统的界面搭建,其中Layui框架中的Layer组件在项目中会用得非常多.且有些功能会遇到子窗口完成数据的提交后,进行关闭同时会刷新父窗口的数据,所以此文章分享一 ...

  8. 主窗口刷新,子窗口闪烁

    问题:主窗口刷新,子窗口闪烁 解决方案:主窗口创建时,传WS_CLIPCHILDREN 转载于:https://www.cnblogs.com/chunyou128/p/3739193.html

  9. VC窗口自由的调整大小的实现

    VC窗口自由的调整大小的实现.具体代码和详细的英文说明,大家可以看 原文 ,本篇是使用后的具体细节说明. 1,将在对话框的属性里设置 边框可调整大小的 2,在主窗体cpp添加 #include Eas ...

最新文章

  1. javaweb项目中发布webservices服务
  2. python基础---闭包、装饰器
  3. PHP的serialize与json_encode
  4. 关于storm0.10.0版本的一个小bug
  5. OPGL+GLFW+GLEW配置详细步骤
  6. 进腾讯实习要交钱?腾讯2021年开除70名员工,拉黑13家公司
  7. tshark筛选、过滤特定ip的数据包
  8. numpy 矩阵拼接_Python实践代码总结第10集(Numpy)
  9. securecrt安装mysql_centos安装mysql(for 小白)
  10. ycl-django网站搭建(持续更新中)
  11. 银行数字化转型导师坚鹏:《银行业同业竞争策略分析》
  12. 876. 链表的中间结点【我亦无他唯手熟尔】
  13. SAP PP 空bom 清单及处理
  14. 新来的CTO规定所有接口都用 post 请求...
  15. C/C++中的日期和时间
  16. sql语句练习题,答案
  17. Linux中怎么搭建Discuz论坛
  18. 文件和文件流——文件转码失败conversion failed: input document is null after loading
  19. s3550(config-if)#switchport mode access 的解释
  20. 网络抓包与流量在线分析系统的设计与实现-基于libpcap在MacOS上实现 记录这愉快(DT)的一周

热门文章

  1. c# wpf listbox 高度_WPF快速入门系列(1)——WPF布局概览
  2. 实战:SQL sever如何实现同一列上下行运算?
  3. OpenLayers中地图缩放级别的设置方法
  4. 协程在Socket上的应用
  5. linux在执行时提示无此文件的一个处理办法
  6. 04-String课后动手动脑
  7. jquery-懒加载技术(简称lazyload)
  8. 【Beta阶段】M2事后分析
  9. HTML5新增的标签
  10. struts2+spring3.2简单demo