窗口刷新问题(WM_PAINT、BeginPaint、EndPaint的说明

在某些情况下,显示区域的一部分被临时覆盖,Windows试图保存一个显示区域,并在以后恢复它,但这不一定能成功。在以下情况下,Windows可能发送WM_PAINT消息:
  Windows擦除覆盖了部分窗口的对话框或消息框。
  菜单下拉出来,然后被释放。
  显示工具提示消息。
  在某些情况下,Windows总是保存它所覆盖的显示区域,然后恢复它。这些情况是:
  鼠标光标穿越显示区域。
  图标拖过显示区域。
  处理WM_PAINT消息要求程序写作者改变自己向显示器输出的思维方式。程序应该组织成可以保留绘制显示区域需要的所有信息,并且仅当「响应要求」-即Windows给窗口消息处理程序发送WM_PAINT消息时才进行绘制。如果程序在其它时间需要更新其显示区域,它可以强制Windows产生一个WM_PAINT消息。这看来似乎是在屏幕上显示内容的一种舍近求远的方法。但您的程序结构可以从中受益。
  1. 系统何时发送WM_PAINT消息?
  系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些动作都是由 系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过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是否为空等。
  2. 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函数,他们可是一对的。
  3.重画函数 InvalidateRect,UpdateWindow, RedrawWindow的区别
  InvalidateRect是通过线程的消息队列来发送刷新消息,是最常用的。
  UpdateWindow是直接调用窗口函数立即响应刷新消息,使窗口刷新消息优先被响应(消息队列中如果没有WM_PAINT消息就什么都不执行),一般是在ShowWindow之后调用。
  RedrawWindow相当于先调用InvalidateRect,紧接着又调用UpdateWindow,此外RedrawWindow还提供了一些前两者没法做到的功能。
  补充几点:
  1.WM_Paint 是一个被动消息,不能通过普通的方法简单的 sendmessage WM_paint 了事这是不行的;但通过消息由程序员引发不是不可能;通过几个特殊的常数可以做到,不过要到delphi下找
  2.sendmessage 可以将消息发送到消息队列;但windows会自动判断是否存在无效的画图区域;如果存在无效的画图区域,则可能会重画,反之则弃用该消息.
  3.可以使用 InvalidateRect 等几个APi将屏幕上任意一个个矩形区域设置为无效区域,在UpdateWindow后调用后,windows会自动查找是否存在无效,并重画,该矩形区域;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值