Invalidate() InvalidateRect() 与 UpdateWindow()

Posted onewayheaven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Invalidate() InvalidateRect() 与 UpdateWindow()相关的知识,希望对你有一定的参考价值。

按引:Invalidate在消息队列中加入一条WM_PAINT消息,其无效区为整个客户区。而UpdateWindow直接发送一个WM_PAINT消息,其无效区范围就是消息队列中WM_PAINT消息(最多只有一条)的无效区。效果很明显,调用Invalidate之后,屏幕不一定马上更新,因为WM_PAINT消息不一定在队列头部,而调用UpdateWindow会使WM_PAINT消息马上执行的,绕过了消息队列。如果你调用Invalidate之后想马上更新屏幕,那就加上UpdateWindow()这条语句。

 

UpdateData():

    当你使用了ClassWizard建立了控件和变量之间的联系后:当你修改了变量的值,而希望对话框控件更新显示,就应该在修改变量后调用UpdateData(FALSE);如果你希望知道用户在对话框中到底输入了什么,就应该在访问变量前调用UpdateData(TRUE),将控件的输入映射到变量中。

Invalidate():
     该函数的作用是使整个窗口客户区无效窗口的客户区无效意味着需要重绘。例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。

InvalidateRect():
    该函数的功能与Invalidate基本一样,不同的是,它是使指定的某个区域无效,需要输入一个区域,如果参数为NULL,则设置整个窗口为无效区。

UpdateWindow():
     UpdateWindow( )的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。

UpdateWindow:如果有无效区,则马上sending a WM_PAINT message到窗口处理过程,不进消息队列进行排队等待,立即刷新窗口,否则,什么都不做。 
InvalidateRect:设置无效区,如果为NULL参数,则设置整个窗口为无效区。当应用程序的那个窗口的消息队列为空时,则sending a WM_PAINT message(即使更新区域为空).在sending a WM_PAINT message的所有InvalidateRect的更新区域会累加。

1:设置无效区 
InvalidateRect 

2:立即刷新 
UpdateWindow() 

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

调用UpdateWindow()时将会发送一个WM_PAINT消息,而应用程序在接收到WM_PAINT消息后,将自动地调用Invalidate()。所以,在程序代码中,不一定要出现Invalidate()!

UpdateWindow()就是立即发送WM_PAINT消息,updateWindow要求系统对区域进行立即重绘,其只对声明为无效的区域起作用,而Invalidate()是声明无效区域的方式之一。

Invalidate()表示客户区域无效,在下次WM_PAINT发生时重绘。而WM_PAINT是由系统进行维护的,每当CWnd的更新区域不为空,并且在应用程序的窗口消息队列中没有其它消息时,Windows就发送一条WM_PAINT消息

Invalidat最后也是调用InvalidatRect。

RedrawWindow 强制刷新,会调用WM_PAINT,但如果你强制刷新的部分不存在就不会调用WM_PAINT。若不带任何参数,则本窗口全部刷新。

*****************************************************************************************************************************************

看到有人在网上提出问题,他在Invalidate后面又写了绘图的函数但是没有执行,这是因为invalidate执行过以后就转到PAINT命令了,所以后面的都没有显示。

也终于想通我绘的图一直在闪啊闪,这是因为我在PAINT里面用到了Invalidate()函数,所以他不停的自嵌套,导致绘的图在不停的闪。

总之:Invalidate让客户区处于可以重画的状态,而UpdateWindow开始重画,但是它首先需判断客户区是否为空,不空则UpdateWindow不执行,为空才执行重画。

*********************************************************************************************************************************************

在刷新窗口时经常要调用重绘函数MFC提供了三个函数用于窗口重绘
     InvalidateRect(&Rect)
    Invalidate()
     UpdateWindow()
       当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化)WM_NVPAINT系统会自己搞定WM_PAINT消息对应的函数是OnPaint(),它是系统默认的接受WM_PAINT消息的函数,但我们一般在程序中做重绘时都在OnDraw函数中进行的,因为在视图类ONPAINT函数中调用了ONDRAW函数。
       CView默认的标准的重画函数
          void CView::OnPaint()
             {  
                   CPaintDC dc(this);   
                   OnPreparDC(&dc);  
                   OnDraw(&dc); //调用了OnDraw

            } 
        上面讲到InvalidateRect(&Rect) 和 Invalidate()。两个函数形式和功能差不多,但Invalidate是使得整个窗口形成无效矩形,而InvalidateRect(&Rect)是使得指定的区域无效。Invalidate()申明无效,等待WM_PAINT消息以便重绘,队列中无其他消息时系统会自动发送。而UpdateWindow()会立即发送WM_PAINT,不过在它发送前,先调用GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制区域,如果没有则不发送消息。RedrawWindow()RedrawWindow()则是具有Invalidate()和UpdateWindow()的双特性。声明窗口的状态为无效,并立即更新窗口,立即调用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消息紧密相关。试一试在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消息了。另外要注意的一点是,BeginPaint只能在WM_PAINT处理函数中使用

 

OnDraw,一般是收到WM_PAINT消息时调用,所以应用程序一般通过Invalidate产生WM_PAINT消息来间接调用OnDraw。当窗体无效等情况下,window也会产生WM_PAINT消息,这时OnDraw 也被间接调用。 
OnUpdate CView提供的一个方法,一般当文档修改时调用,应用程序框架在CView::OnInitialUpdate CDocument::UpdateAllViews 的默认实现中都会调用 
OnUpdateOnUpdate的默认实现是通过Invalidate产生WM_PAINT,这时OnDraw又被调用了。 

OnDraw除了你和应用程序框架间接调用外,window还可能间接调用它。 
OnUpdate一般只有你的程序和应用程序框架会调用的。当然它的默认实现你可以改变的

**********************************************************************************************************************

OnInitUpdateVIEW的初始化 
OnUpdate是文档多视时,响应其它视图的改变 
OnDrawOnPaint都是绘图。OnPaint调用OnDraw,并且调用OnPrepareDC 
--------------------------------------------------------------- 

以上是关于Invalidate() InvalidateRect() 与 UpdateWindow()的主要内容,如果未能解决你的问题,请参考以下文章

session.invalidate()和session.abandon()有啥不同,该如何选择?

收集统计信息中no_invalidate选项对执行计划的影响

invalidate和requestLayout

android.invalidate()

invalidate() 不刷新视图

invalidate和requestLayout方法源码分析