在刷新窗口時經(jīng)常要調(diào)用重繪函數(shù)MFC提供了三個函數(shù)用于窗口重繪
InvalidateRect(&Rect)
Invalidate()
UpdateWindow()
當(dāng)需要更新或者重繪窗口時,一般系統(tǒng)會發(fā)出兩個消息WM_PAINT(通知客戶區(qū)有變化)和WM_NCPAINT(通知非客戶區(qū)有變化)WM_NVPAINT系統(tǒng)會自己搞定WM_PAINT消息對應(yīng)的函數(shù)是OnPaint(),它是系統(tǒng)默認(rèn)的接受WM_PAINT消息的函數(shù),但我們一般在程序中做重繪時都在OnDraw函數(shù)中進(jìn)行的,因為在視圖類ONPAINT函數(shù)中調(diào)用了ONDRAW函數(shù)。
CView默認(rèn)的標(biāo)準(zhǔn)的重畫函數(shù)
void CView::OnPaint()
{
CPaintDC dc(this);
OnPreparDC(&dc);
OnDraw(&dc); //調(diào)用了OnDraw}
上面講到InvalidateRect(&Rect)Invalidate()兩個函數(shù)形式和功能差不多但I(xiàn)nvalidate是使得整個窗口無效,形成無效矩形,而InvalidateRect(&Rect)是使得指定的區(qū)域無效 Invalidate()申明無效,等待WM_PAINT消息以便重繪,隊列中無其他消息時系統(tǒng)會自動發(fā)送UpdateWindow()會立即發(fā)送WM_PAINT,不過在它發(fā)送前,先調(diào)用GetUpdateRect(hWnd,NULL,TRUE)看有無可繪制區(qū)域,如果沒有則不發(fā)送消息 RedrawWindow()RedrawWindow()則是具有Invalidate()和UpdateWindow()的雙特性。聲明窗口的狀態(tài)為無效,并立即更新窗口,立即調(diào)用WM_PAINT消息處理。
系統(tǒng)為什么不在調(diào)用Invalidate時發(fā)送WM_PAINT消息呢?又為什么非要等應(yīng)用消息隊列為空時才發(fā)送WM_PAINT消息呢?這是因為系統(tǒng)把在窗口中的繪制操作當(dāng)作一種低優(yōu)先級的操作,于是盡 可能地推后做。不過這樣也有利于提高繪制的效率:兩個WM_PAINT消息之間通過InvalidateRect和InvaliateRgn使之失效的區(qū)域就會被累加起來,然后在一個WM_PAINT消息中一次得到 更新,不僅能避免多次重復(fù)地更新同一區(qū)域,也優(yōu)化了應(yīng)用的更新操作。像這種通過InvalidateRect和InvalidateRgn來使窗口區(qū)域無效,依賴于系統(tǒng)在合適的時機(jī)發(fā)送WM_PAINT消息的機(jī) 制實際上是一種異步工作方式,也就是說,在無效化窗口區(qū)域和發(fā)送WM_PAINT消息之間是有延遲的;有時候這種延遲并不是我們希望的,這時我們當(dāng)然可以在無效化窗口區(qū)域后利用SendMessage 發(fā)送一條WM_PAINT消息來強(qiáng)制立即重畫,但不如使用Windows GDI為我們提供的更方便和強(qiáng)大的函數(shù):
UpdateWindow和RedrawWindow。UpdateWindow會檢查窗口的 Update Region,當(dāng)其不為空時才發(fā)送WM_PAINT消息;RedrawWindow則給我們更多的控制:是否重畫非客戶區(qū)和背景,是否總是發(fā)送 WM_PAINT消息而不管Update Region是否為空等。 BeginPaint和WM_PAINT消息緊密相關(guān)。試一試在WM_PAINT處理函數(shù)中不寫B(tài)eginPaint會怎樣?程序會像進(jìn)入了一個死循環(huán)一樣達(dá)到驚人的CPU占用率,你會發(fā)現(xiàn)程序總在處理一個接 一個的WM_PAINT消息。這是因為在通常情況下,當(dāng)應(yīng)用收到WM_PAINT消息時,窗口的Update Region都是非空的(如果為空就不需要發(fā)送WM_PAINT消息了),BeginPaint的一個作用就是把該Update Region置為空,這樣如果不調(diào)用BeginPaint,窗口的Update Region就一直不為空,如前所述,系統(tǒng)就會一直發(fā)送WM_PAINT消息。 BeginPaint和WM_ERASEBKGND消息也有關(guān)系。當(dāng)窗口的Update Region被標(biāo)志為需要擦除背景時,BeginPaint會發(fā)送WM_ERASEBKGND消息來重畫背景,同時在其返回信息里有一個標(biāo)志表明窗口背景是否被重畫過。當(dāng)我們用InvalidateRect和InvalidateRgn來把指定區(qū)域加到Update Region中時,可以設(shè)置該區(qū)域是否需要被擦除背景,這樣下一個BeginPaint就知道是否需要發(fā)送WM_ERASEBKGND消息了。另外要注意的一點是,BeginPaint只能在WM_PAINT處理函數(shù)中使用。