MFC窗口強制重繪

轉自:https://www.cnblogs.com/skywatcher/p/3567754.html        

MFC提供了三個重繪函數:  Invalidate()、UpdateWindow()和RedrawWindow()。

強制窗口重繪只用一個RedrawWindow()即可,Invalidate() 與UpdateWindow()兩個函數組合使用纔會引起重繪。

重繪之後你又會發現圖形閃爍,看這裏來解決:https://www.cnblogs.com/xingrun/p/3395589.html。

1. 重繪消息

當需要更新或者重繪窗口時,一般系統會發出兩個消息WM_PAINT(通知客戶區有變化)和WM_NCPAINT(通知非客戶區有變化)

  • WM_NCPAINT系統會自己搞定
  • WM_PAINT消息對應的函數是OnPaint(),它是系統默認的接受WM_PAINT消息的函數,但我們一般在程序中做重繪時都在OnDraw函數中進行的,因爲在ONPAIN函數中調用了ONDRAW函數。
    ///CView默認的標準的重畫函數
    void CView::OnPaint()
    {
       CPaintDC dc(this);
        OnPreparDC(&dc);
        OnDraw(&dc); //調用了OnDraw
    }

2. 重繪函數

  • InvalidateRect(&Rect)和Invalidate()兩個函數形式和功能差不多,這兩個函數並不會直接引起窗口或試圖重繪,需要在處理WM_PAINT消息是纔會引起無效窗口或無效矩形重繪。但Invalidate是使得整個窗口無效,形成無效矩形,而InvalidateRect(&Rect)是使得指定的區域無效。
  • Invalidate()申明無效,等待WM_PAINT消息以便重繪,隊列中無其他消息時系統會自動發送。void Invalidate( BOOL bErase = TRUE ); 該函數的作用是使整個窗口客戶區無效。窗口的客戶區無效意味着需要重繪,例如,如果一個被其它窗口遮住的窗口變成了前臺窗口,那麼原來被遮住的部分就是無效的,需要重繪。這時Windows會在應用程序的消息隊列中放置WM_PAINT消息。MFC爲窗口類提供了WM_PAINT的消息處理函數OnPaint,OnPaint負責重繪窗口。視圖類有一些例外,在視圖類的OnPaint函數中調用了OnDraw函數,實際的重繪工作由OnDraw來完成。參數bErase爲TRUE時,重繪區域內的背景將被擦除,否則,背景將保持不變。
  • UpdateWindow()會立即發送WM_PAINT,不過在它發送前,先調用GetUpdateRect(hWnd,NULL,TRUE)看有無可繪製區域,如果沒有則不發送消息。RedrawWindow()則是具有Invalidate()和UpdateWindow()的雙特性。聲明窗口的狀態爲無效,並立即更新窗口,立即調用WM_PAINT消息處理。

3. 系統消息機制

 

      系統爲什麼不在調用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處理函數中使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章