




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、打斷你們一下,又一位大神,心羽 ,跟樓上完全兩種不同的風(fēng)格,他主張全部用貼圖解決問(wèn)題,也讓我大開(kāi)眼界,謝謝你,讓我們用最熱烈的掌聲歡迎他, 自繪對(duì)話框重繪是會(huì)閃, 請(qǐng)問(wèn)如何避免閃屏? - VC/MFC / 界面 收藏人:峰中之王 2010-10-23 | 閱: 轉(zhuǎn): | 大 中 小 | 分享 如果一個(gè)程序出現(xiàn)閃爍現(xiàn)象,會(huì)讓人覺(jué)得程序編寫人員很馬虎,缺乏對(duì)細(xì)節(jié)的足夠重視。Windows程序的任何部分都沒(méi)有任何理由出現(xiàn)
2、閃爍現(xiàn)象。這篇文章的目的是告訴讀者如何使用相關(guān)的技術(shù)防止窗口出現(xiàn)閃爍效果。 什么是閃爍閃爍可以這樣定義:當(dāng)后面一幅圖像以很快的速度畫在前面一幅圖像上時(shí),在后面圖像顯示前,你可以很快看到前面那一個(gè)圖像,這樣的現(xiàn)象就是閃爍。我認(rèn)為,閃爍會(huì)讓使用者對(duì)程序很不滿,原因是:如果用戶接口編碼如此糟糕,那么程序的其他部分呢,如何能相信數(shù)據(jù)的正確性呢?一個(gè)具有平滑,快速相應(yīng)的程序會(huì)給用戶帶來(lái)信心,這個(gè)道理很簡(jiǎn)單。 程序出現(xiàn)閃爍可以由多種形式造成,最常見(jiàn)的原因是窗口大小發(fā)生改變時(shí),其內(nèi)容重畫造成閃爍。 僅僅畫一次這是一個(gè)黃金法則,在任何計(jì)算機(jī)(Windows或者你使用的任何操
3、作系統(tǒng))上處理畫法邏輯都需要遵循,即永遠(yuǎn)不要將同一像素畫兩次。一個(gè)懶惰的程序員常常不愿意在畫法邏輯上投入過(guò)多精力,而是采用簡(jiǎn)單的處理邏輯。要避免閃爍,就需要確保不會(huì)出現(xiàn)重復(fù)繪制的情況發(fā)生。現(xiàn)在,WIndows和計(jì)算機(jī)還是很笨的,除非你給他們指令,否則他們不會(huì)做任何事情。如果閃爍的現(xiàn)象發(fā)生,那是因?yàn)槟愕某绦蚩桃獾囟嗬L制了屏幕的某些區(qū)域造成的. 這個(gè)現(xiàn)象可能是因?yàn)橐恍┟鞔_的命令,或者一些被你忽視了的地方。如果程序有閃爍的現(xiàn)象出現(xiàn),你需要你知道如何找到好的方案去解決這個(gè)問(wèn)題。 WM_ERASEBKGND通常,首先需要懷疑的是WM_ERASEBKGND消息。當(dāng)一個(gè)窗口的背景需要被擦除時(shí),這
4、個(gè)消息會(huì)被發(fā)送。這是因?yàn)榇翱诘睦L畫通常經(jīng)歷了兩個(gè)過(guò)程 WM_ERASEBKGND: 清除背景 WM_PAINT: 在上面繪制內(nèi)容 這兩個(gè)過(guò)程讓窗體在繪制內(nèi)容時(shí)變得很簡(jiǎn)單,即:每次當(dāng)收到WM_PAINT消息時(shí),你知道已經(jīng)有了一個(gè)新畫布等待去繪制。然而,畫窗口兩次(一次是通過(guò)WM_ERASEBKGND畫背景,另外一次是WM_PAINT)將會(huì)導(dǎo)致窗口出現(xiàn)比較糟糕的閃爍現(xiàn)象。只要看看標(biāo)準(zhǔn)的編輯框-打開(kāi)Windows的寫字板并改變窗口大小,就可以看到那種閃爍的效果。 那么,如何避免窗口背景的重刷呢?有如下兩種方法: 設(shè)置窗口背景刷子為NULL(
5、當(dāng)注冊(cè)Windows類時(shí),設(shè)置WNDCLASS結(jié)構(gòu)中的hbrBackground成員為零) 在WM_ERASEBKGND消息處理時(shí) 返回非零值 以上任何一種方法都可以阻止WM_ERASEBKGND 消息去清除窗口。其中,第二個(gè)方案的通??梢砸匀缦麓a實(shí)現(xiàn): case WM_ERASEBKGND:return 1; 當(dāng)你標(biāo)記窗口內(nèi)容無(wú)效并試圖更新時(shí),還有如下辦法可以防止WM_ERASEBKGND消息:InvalidateRect函數(shù)的最后一個(gè)參數(shù)可以指明在下一次窗口重畫時(shí),是否窗口的部分背景會(huì)被重刷。將該參數(shù)置為False可以防止當(dāng)窗口需要重畫時(shí)系
6、統(tǒng)發(fā)出WM_ERASEBKGND消息。 InvalidateRect(hwnd, &rect, FALSE); 不該畫的時(shí)候一定不要畫有一個(gè)比較普遍的現(xiàn)象:即使窗口中只有一個(gè)小的部分發(fā)生了改變,往往所有的部分都會(huì)被重畫。比如,經(jīng)常地,當(dāng)窗口大小被改變時(shí),一些(不是所有)的程序會(huì)重畫所有的窗口。通常,這是個(gè)是不必要的,這是因?yàn)楫?dāng)窗口大小被改變時(shí),經(jīng)常是之前窗口的內(nèi)容是不變的,僅僅是改變大小造成的一個(gè)小的邊界區(qū)域需要重畫。此時(shí),沒(méi)有必要重畫所有區(qū)域。如果在這里多注意,多考慮,就可以使用好的算法以使得一次只有最小的部分被畫。 系統(tǒng)中每個(gè)窗口都有更新區(qū)域。這
7、個(gè)區(qū)域描述了窗口中變得無(wú)效需要重畫的地方。如果一個(gè)窗口僅僅其需要更新的區(qū)域,不多繪制其他地方,那么窗口的繪制效果將會(huì)非??臁?#160; 有幾種方法可以獲得窗口的更新區(qū)域。通過(guò)GetUpdateRgn 函數(shù)可以獲得準(zhǔn)確的更新區(qū)域,這個(gè)函數(shù)返回的結(jié)果可以使矩形的區(qū)域也可以是非矩形的區(qū)域。通過(guò)GetUpdateRect 函數(shù)可以獲得需要更新的最小矩形區(qū)域。通常使用矩形的更新區(qū)域比較容易。第三個(gè)方法是在BeginPaint/EndPaint中得到PAINTSTRUCT 結(jié)構(gòu),從而得到準(zhǔn)確的更新區(qū)域信息。 一個(gè)常規(guī)的畫法函數(shù)是這樣的: PAINTSTRUCT ps;HDC hd
8、c;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);/ do paintingEndPaint(hwnd, &ps);return 0;BeginPaint函數(shù)初始化PS(PAINTSTRUCT)結(jié)構(gòu),其中,成員rcPaint是一個(gè)RECT結(jié)構(gòu),描述了包含了需要更新的最小矩形區(qū)域(就像GetWindowRect函數(shù))。如果僅僅在這個(gè)矩形區(qū)域上繪制窗口,速度上繪有很好地提高。 現(xiàn)在,當(dāng)使用BeginPaint/EndPaint時(shí)Windows會(huì)自動(dòng)剪切掉畫在更新區(qū)域外面的部分。這意味著,你沒(méi)有機(jī)會(huì)畫到更新區(qū)域以外的地方??赡苣銜?huì)認(rèn)為
9、,如果是這樣的話,花功夫確保代碼不試圖畫到更新區(qū)域外是沒(méi)有意義的,反正沒(méi)有畫出任何東西來(lái)。然而,你仍然可以避免不必要的API調(diào)用和相關(guān)計(jì)算,所以,我認(rèn)為放一些精力在如何工作地更快上是絕對(duì)值得的。 如果還是不能解決有些時(shí)候,當(dāng)你花了很多努力去考慮非常好的畫法時(shí),發(fā)現(xiàn)窗口還是會(huì)被全部刷新。這通常是由兩個(gè)Window 類的屬性造成的:CS_VREDRAW 和 CS_HREDRAW。如果有其中一個(gè)標(biāo)志被設(shè)置時(shí),那么當(dāng)窗口水平或者豎直方向有大小被改變時(shí),其內(nèi)容每次都會(huì)被重新刷新。所有,你需要關(guān)掉這兩個(gè)標(biāo)志,解決的唯一的方式是在創(chuàng)建窗體和窗體類被注冊(cè)時(shí),確保這兩個(gè)屬性不被設(shè)置。 W
10、NDCLASSEX wc;wc.cbSize = sizeof(wc);wc.style = 0; /* CS_VREDRAW | CS_HREDRAW; */ .RegisterClassEx(&wc);上面的例子描述了當(dāng)窗體類被注冊(cè)時(shí),這兩個(gè)屬性不被設(shè)置的實(shí)現(xiàn)方法。有一點(diǎn)需要注意:如果主窗體有了這兩個(gè)屬性,即使子窗體沒(méi)有重畫標(biāo)志,會(huì)導(dǎo)致所有子窗體在其大小被改變時(shí)會(huì)被重繪??梢酝ㄟ^(guò)以下方式避免這個(gè)情況發(fā)生: 剪切子窗體有時(shí),閃爍的原因是因?yàn)楫?dāng)重畫時(shí),父窗體沒(méi)有剪切其子窗體區(qū)域。這樣的結(jié)果導(dǎo)致,整個(gè)父窗口內(nèi)容被重畫,而子窗體又被顯示在了上面(造成閃爍)。這個(gè)可以
11、通過(guò)在父窗體上設(shè)置WS_CLIPCHILDREN 來(lái)解決。當(dāng)這個(gè)標(biāo)志被設(shè)置時(shí),被子窗體占據(jù)的任何區(qū)域?qū)?huì)被排除在更新區(qū)域外。因此,即使你嘗試在子窗體所在的位置上繪制(父窗口的內(nèi)容),BeginPaint中的剪切區(qū)域也會(huì)阻止其繪制效果。 雙緩沖和內(nèi)存設(shè)備描述表(Memory Device Context, 簡(jiǎn)稱Memory-DC)常見(jiàn)的徹底避免閃爍的方法是使用雙緩沖。其基本的思路是:將窗體的內(nèi)容畫在屏幕外的一個(gè)緩沖區(qū)內(nèi),然后,將該緩沖區(qū)的內(nèi)容再傳遞到屏幕上(使用BilBlt函數(shù))。這是一個(gè)非常好的減少閃爍的方法,但是經(jīng)常被濫用,特別是當(dāng)程序員并不真正地理解如何有效地繪制窗口時(shí)。
12、60; 典型的雙緩沖代碼如下:HDC hdcMem;HBITMAP hbmMem;HANDLE hOld;PAINTSTRUCT ps;HDC hdc;.case WM_PAINT:/ Get DC for windowhdc = BeginPaint(hwnd, &ps);/ Create an off-screen DC for double-bufferinghdcMem = CreateCompatibleDC(hdc);hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);hOld = SelectObject
13、(hdcMem, hbmMem);/ Draw into hdcMem/ Transfer the off-screen DC to the screenBitBlt(hdc, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);/ Free-up the off-screen DCSelectObject(hdcMem, hOld);DeleteObject(hbmMem);DeleteDC (hdcMem);EndPaint(hwnd, &ps);return 0; 這個(gè)方法比較慢,因?yàn)樵诿看未绑w需要重畫的時(shí)候內(nèi)存設(shè)備
14、描述表(Memory-DC)都需要被重新創(chuàng)建。更有效的方法是,僅僅創(chuàng)建內(nèi)存設(shè)備描述表(Memory-DC)一次,并使其足夠大到能滿足任何時(shí)候的整個(gè)窗體刷新。當(dāng)程序結(jié)束時(shí),再銷毀這個(gè)內(nèi)存設(shè)備描述表(Memory-DC)。這兩種方法都存在對(duì)內(nèi)存開(kāi)銷的問(wèn)題,特別是如果內(nèi)存設(shè)備描述表(Memory-DC)是針對(duì)真?zhèn)€屏幕的大小。雙緩沖也需要兩倍的時(shí)間去畫。這是因?yàn)槠涞谝淮问窃趦?nèi)存設(shè)備描述表(Memory-DC)上畫,然后再使用BitBlt畫回到屏幕上。當(dāng)然,好的顯卡會(huì)使BitBlt更快,但是仍然會(huì)耗CPU 時(shí)間。 如果程序需要顯示相當(dāng)復(fù)雜的信息,比如像網(wǎng)頁(yè),那么你應(yīng)該使用內(nèi)存設(shè)備描述表(Me
15、mory-DC)。比如IE,如果不使用雙緩沖,是沒(méi)有辦法在繪制網(wǎng)頁(yè)時(shí)不閃爍的。 沒(méi)有必要將雙緩沖技術(shù)用于整個(gè)窗體的繪制中。可以這樣設(shè)想,窗口中僅僅有一個(gè)小部分包含了復(fù)雜的圖形對(duì)象(比如半透明的位圖或者其他)。你應(yīng)該將內(nèi)存設(shè)備描述表(Memory-DC)僅僅用于著一個(gè)小區(qū)域,其他區(qū)域使用常規(guī)的方法。 有時(shí),通過(guò)仔細(xì)的思考,經(jīng)??梢员苊馐褂秒p緩沖而直接將結(jié)果畫到屏幕上。只要你不破壞黃金法則,即“永遠(yuǎn)不要將一個(gè)像素畫兩次”,就可以防止閃爍的出現(xiàn)。 避免過(guò)度繪制我想說(shuō)的關(guān)于這個(gè)話題是這樣的:有一個(gè)需要自己定義畫法的窗體的標(biāo)題欄。首先,你畫了標(biāo)題,接著在上面畫一些其他的圖形?,F(xiàn)在
16、,只要標(biāo)題需要被重畫,就會(huì)出現(xiàn)閃爍現(xiàn)象。這是因?yàn)槟銢](méi)有合乎黃金法則。這里,標(biāo)題被很快地顯示在其他圖形在上面繪制時(shí),導(dǎo)致了閃爍。 有兩種技術(shù)可以組織這種類型的閃爍。第一個(gè)是使用剪切,第二個(gè)是使用你的大腦。 使用剪切時(shí),你可以使用ExcludeClipRect 函數(shù)在設(shè)備描述表中去標(biāo)記一個(gè)特定的區(qū)域。當(dāng)一個(gè)區(qū)域被標(biāo)記上時(shí),即使在該區(qū)域上面重畫也不會(huì)產(chǎn)生效果。一旦背景已經(jīng)被繪制了,可以通過(guò)SelectClipRgn移掉該標(biāo)記的區(qū)域,其他圖形能被畫到前面標(biāo)記的區(qū)域上。通過(guò)準(zhǔn)確的標(biāo)記(剪切),可以在很多時(shí)候被避免過(guò)度繪制。 另外一個(gè)方案就是找更聰明的解決辦法。比如,當(dāng)
17、你需要畫一個(gè)表格,通常應(yīng)該先畫空的背景,再畫網(wǎng)格線從而產(chǎn)生表格。但是,這個(gè)方法會(huì)使網(wǎng)格線產(chǎn)生閃爍,這是因?yàn)樵诰W(wǎng)格線被畫之前,下面背景被很快地顯示了一下。然而可以使用不同的做法達(dá)到想要的結(jié)果。即,不是一次畫一個(gè)大的空背景,而是畫一系列的空方塊,每一個(gè)方塊邊是被一個(gè)像素的寬度分開(kāi)。這樣,當(dāng)畫網(wǎng)格線時(shí),他們剛好能被畫到一個(gè)之前沒(méi)有畫過(guò)的地方。其結(jié)果是不會(huì)有閃爍現(xiàn)象,因?yàn)闆](méi)有像素被畫了超過(guò)兩次。 使用你的頭腦去想一個(gè)好的算法可能需要長(zhǎng)一點(diǎn)的時(shí)間,但是卻是值得的,因?yàn)檫@能讓結(jié)果更好。 結(jié)論希望你再也不會(huì)問(wèn):“為什么我的窗體會(huì)閃爍”這樣的問(wèn)題。我已經(jīng)講解了閃爍的主要原因和解決辦法。
18、如果你遇到了閃爍的問(wèn)題,你應(yīng)該能找到原因并且使用這里提到的技術(shù)來(lái)解決了窗口和控件閃爍解決方案 (2010-07-05 09:42:38)轉(zhuǎn)載標(biāo)簽: 窗口閃爍 it分類: 開(kāi)發(fā) 對(duì)于MFC程序員來(lái)說(shuō)做UI開(kāi)發(fā)是痛苦的事情,不過(guò)大多數(shù)情況下我們都需要做這件事情,因?yàn)镸FC自帶的控件實(shí)在是太簡(jiǎn)陋了。這時(shí)候我們多半會(huì)涉及到自繪控件,隨之而來(lái)的很可能就是窗口和控件的閃爍問(wèn)題。這篇文章希望對(duì)MFC的窗口和控件閃爍問(wèn)題做一個(gè)盡量全面的總結(jié)。 一、閃爍的原因
19、; 引起閃爍的原因很多,以至于網(wǎng)上有n多種解決閃爍問(wèn)題的方法;如果你按照某一種方法做了仍然沒(méi)有解決你的問(wèn)題,請(qǐng)不要認(rèn)定這個(gè)方法有問(wèn)題,而是你沒(méi)有對(duì)上號(hào)。如果你對(duì)這個(gè)解釋不滿意的話,我們就來(lái)深究一下到底是什么引起了閃爍。從原理上講,閃爍是因?yàn)槠聊簧线B續(xù)的兩次或多次輸出畫面差別比較大引起的,這是最根本的原因。因此如果窗口繪制差別不大,即使刷新再頻繁,也不會(huì)引起閃爍。但是差別較大的畫面輸出一定會(huì)引起閃爍嗎?還有一個(gè)因素要考慮進(jìn)來(lái),就是屏幕的刷新頻率。根據(jù)顯卡和顯示器的不同,屏幕的刷新周期是不一樣的,雖然這個(gè)參數(shù)的差別對(duì)界面開(kāi)發(fā)的影響幾乎可以忽略,但是如果你真的從思想
20、上理解了這一點(diǎn),你就會(huì)立即明白為什么雙緩沖技術(shù)能夠幫助我們解決一部分閃爍問(wèn)題。 二、再談閃爍的原因 雖然第一部分的描述對(duì)我們有一些啟發(fā),但我們還是應(yīng)該更深入一些!哪些情況下會(huì)導(dǎo)致我們的窗口或控件輸出連續(xù)的差別較大的繪制界面呢? 1、繪制界面太復(fù)雜,一個(gè)刷新周期內(nèi)繪制不完,每次都輸出一部分繪制結(jié)果,導(dǎo)致幾次刷新閃爍。 我們的繪制過(guò)程都是通過(guò)很多個(gè)繪制語(yǔ)句組成的,如果這些語(yǔ)句加起來(lái)的時(shí)間大于一個(gè)刷新周期,
21、那么就很可能引起閃爍。通常的解決辦法是去掉中間過(guò)程的刷新,直到最后整體繪制完畢再一次性刷新。是不是似曾相識(shí),這就是雙緩沖技術(shù)的原理!但是有些情況是雙緩沖也無(wú)能為力的,后面再講。 2、繪制過(guò)程很簡(jiǎn)單,但是需要頻繁刷新。 這種情況下我們首先需要弄清楚頻繁刷新的原因是什么,不同的原因?qū)?yīng)不同的解決辦法。但是歸根結(jié)底,我們還是為了減少刷新的次數(shù)或者盡量去掉中間輸出差別較大的繪制輸出。 3、刷新過(guò)程。 對(duì)于窗口或控件的界面
22、顯示,windows系統(tǒng)有一套繪制和刷新的規(guī)則,繪制或刷新的時(shí)機(jī)選擇也是影響閃爍的重要因素。如果再與上面兩條結(jié)合起來(lái),某些情況下引起閃爍的原因確實(shí)非常復(fù)雜。只有我們分析出問(wèn)題所在,才能用正確的方法解決之。 三、幾種消除閃爍的解決方案 1、盡量減少重復(fù)繪制 MFC的窗口和控件刷新有一套很復(fù)雜的規(guī)則,如果我們能深入理解,正確應(yīng)用的話就能避免一部分閃爍。比如盡量用 InvalidateRect() 函數(shù)代替 Invalid
23、ate() 函數(shù),InvalidateRect() 函數(shù)只刷新界面上指定的區(qū)域,如果我們的界面上只有一小部分需要頻繁刷新,那么用這個(gè)函數(shù)代替 Invalidate() 的話,解決閃爍問(wèn)題的效果是非常明顯的。這個(gè)函數(shù)已經(jīng)封裝到MFC的CWnd類中(也有API函數(shù))。 void InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE); 其中,lpRect指向一個(gè)方形區(qū)域,該區(qū)域?qū)⒈惶砑拥叫枰碌膮^(qū)
24、域列表中,bErase指定刷新時(shí)是否更新區(qū)域背景。 如果我們需要刷新的區(qū)域是不規(guī)則的,比如是幾個(gè)區(qū)域的組合,或者是某區(qū)域中去掉一部分,這時(shí)候用 InvalidateRect() 不能滿足我們的需求,我們可以用 InvalidateRgn() 函數(shù)。 void InvalidateRgn (CRgn* pRgn, BOOL bErase = TRUE); 其中,pRgn指向需要刷新的區(qū)域。下面是一段示例代碼:
25、 Crect rectClient; CRgn rgn1, rgn2; GetClientRect(rectClient); rgn1.CreateRectRgnIndirect(rectClient); rgn2.CreateRectRgnIndirect(m_rectButton);
26、;rgn1.CombineRgn(&rgn1, &rgn2, RGN_XOR); InvalidateRgn(&rgn1, FALSE); 有的時(shí)候我們的窗口上有很多控件,如果是由我們負(fù)責(zé)控件刷新(比如窗口設(shè)置了WS_CLIPCHILDREN風(fēng)格),我們最好判斷不同情況下確實(shí)需要刷新的控件,而不是簡(jiǎn)單的將所有控件全部刷新一遍,以此將閃爍的影響減小到最小。 2、正確選擇窗口重繪時(shí)機(jī)
27、 Windows有很多刷新和重繪的函數(shù),但是他們的特性和運(yùn)行方式不盡相同,我們需要了解調(diào)用這些函數(shù)的注意事項(xiàng),否則很可能因?yàn)閷?shí)際情況跟我們的預(yù)期不同而引起閃爍。 Windows系統(tǒng)是通過(guò)WM_PAINT消息來(lái)通知界面重繪的,該消息一般由系統(tǒng)自動(dòng)產(chǎn)生,比如當(dāng)窗口被創(chuàng)建、改變大小、最大化、移動(dòng)、覆蓋等等,另外當(dāng)UpdateWindow等函數(shù)被調(diào)用時(shí)也會(huì)產(chǎn)生WM_PAINT消息。 當(dāng)窗口重繪時(shí),并不一定整個(gè)窗口區(qū)域都需要刷新,而只是需要更新的那一部分,這部分區(qū)域叫做“無(wú)效區(qū)域
28、”。系統(tǒng)在發(fā)現(xiàn)消息隊(duì)列空閑時(shí)會(huì)檢查無(wú)效區(qū)域,如果存在就會(huì)發(fā)送WM_PAINT消息進(jìn)行刷新。 Invalidate()、InvalidateRect()、InvalidateRgn()這些函數(shù)都只是產(chǎn)生無(wú)效區(qū)域,而并沒(méi)有發(fā)送WM_PAINT消息,也就是說(shuō)我們調(diào)用這些 Invalidate() 函數(shù)時(shí),并不一定會(huì)使窗口立即刷新,而是要等到下次WM_PAINT消息進(jìn)入到消息隊(duì)列時(shí)才行。如果要使重繪立即執(zhí)行,可以調(diào)用 UpdateWindow() 函數(shù)或者 RedrawWindow() 函數(shù)強(qiáng)制刷
29、新。 Windows的窗口重繪時(shí),會(huì)首先判斷是否需要刷新背景,如果需要?jiǎng)t首先刷新窗口背景,然后進(jìn)入OnPaint()函數(shù)進(jìn)行窗口內(nèi)容的繪制。這個(gè)過(guò)程中如果操作不當(dāng),也有可能引起閃爍。當(dāng)我們遇到閃爍問(wèn)題,可以從以上窗口繪制機(jī)制中查找是否某些步驟的操作引起了閃爍。比如我們?cè)趯?duì)一個(gè)CListCtrl控件進(jìn)行頻繁操作時(shí)(比如添加多個(gè)項(xiàng)或者修改內(nèi)容),可以先調(diào)用 SetRedraw(FALSE),在操作全部完成后,再調(diào)用 SetRedraw(TURE) 完成一次性刷新。
30、3、控制窗口背景刷新 Windows窗口背景刷新默認(rèn)情況下是系統(tǒng)幫你完成的,如果我們的窗口繪制內(nèi)容和背景差別比較大,或者在刷新背景和刷新窗口繪制之間有一個(gè)明顯的時(shí)間間隔,就有可能引起閃爍。 這個(gè)時(shí)候我們可能要禁止系統(tǒng)默認(rèn)的背景繪制,而在窗口繪制函數(shù)中自行處理背景。這時(shí)只要重載 OnEraseBkgnd() 函數(shù),并直接返回TRUE就可以了,代碼如下: BOOL CMyWnd:OnEraseBkgnd(CDC* pDC)
31、0; return TRUE; / return CWnd:OnEraseBkgnd(pDC); / 注釋掉默認(rèn)語(yǔ)句 4、雙緩沖 也許你已經(jīng)聽(tīng)說(shuō)過(guò)雙緩沖這種方法了,的確,多數(shù)情況下雙緩沖能很好的解決我們的窗口閃爍問(wèn)題,尤其是涉及到窗口自繪的時(shí)候。雙緩沖的基本原理是
32、首先將復(fù)雜的繪制結(jié)果輸出到內(nèi)存DC上,然后再一次性輸出到真正的窗口DC,這樣就避免了由于繪制時(shí)間占用多個(gè)刷新周期,而導(dǎo)致一次繪制引起短時(shí)間多次輸出產(chǎn)生閃爍。雙緩沖方法結(jié)合上一個(gè)方法,可以解決大部分自繪窗口的閃爍問(wèn)題。具體的雙緩沖示例代碼如下: void CMyWnd:OnPaint() CPaintDC dc(this);
33、160; CRect rectClient; GetClientRect(&rectClient); CDC dcMem;
34、0;CBitmap bmpMem; dcMem.CreateCompatibleDC(&dc); bmpMem.CreateCompatibleBitmap(&dc, rectCli
35、ent.Width(), rectClient.Height(); dcMem.SelectObject(&bmp); / 此處將繪制內(nèi)容輸出到dcMem上 / dcMem.FillR
36、ect(rectClient, &brush); dc.BitBlt(0, 0, rectClient.Width(), rectClient.Height(), &dcMem, 0, 0, SRCCOPY); bmpM
37、em.DeleteObject(); dcMem.DeleteDC(); 5、合理設(shè)置WS_CLIPCHILDREN和WS_CLIPSIBLINGS風(fēng)格 當(dāng)我們的窗口界面有多層窗口組成時(shí)(比如包含多個(gè)控件的對(duì)話框),用到自繪窗口可能會(huì)經(jīng)常碰到閃爍問(wèn)題。因?yàn)槎鄬哟翱跁?huì)涉及到很多遮擋,重繪時(shí)一般涉及到主窗口和子窗口等多個(gè)窗口,而這些窗口的刷新可能不會(huì)在一
38、個(gè)刷新周期內(nèi)完成,從而引起閃爍。這時(shí)我們可以通過(guò)設(shè)置WS_CLIPCHILDREN和WS_CLIPSIBLINGS這兩個(gè)窗口風(fēng)格來(lái)控制刷新行為。 Clip是裁剪的意思,兩個(gè)屬性的具體含義如下: 帶有WS_CLIPCHILDREN風(fēng)格表示裁剪掉子窗口的區(qū)域,即當(dāng)該窗口重繪時(shí),它的子窗口區(qū)域不刷新,而留給子窗口自己去刷新; 帶有WS_CLIPSIBLINGS風(fēng)格(只用于子窗口)表示裁剪掉兄弟窗口的區(qū)域,即當(dāng)該窗口重繪時(shí),與兄弟窗口重疊的區(qū)域?qū)⒉粫?huì)被刷新。
39、0; 根據(jù)這些窗口行為,我們就能優(yōu)化我們的界面刷新,控制一些窗口的刷新時(shí)機(jī),或者減少重疊區(qū)域的重復(fù)刷新。比如當(dāng)對(duì)話框窗口放置了大量控件時(shí),我們可以給對(duì)話框加上WS_CLIPCHILDREN風(fēng)格來(lái)阻止一些不必要的刷新。 6、多層次窗口調(diào)整大小 如果窗口包含很多子窗口,當(dāng)我們調(diào)整窗口大小時(shí),可能要同時(shí)調(diào)整子窗口的位置和大小。此時(shí)若使用 MoveWindow() 或 SetWindowPos() 等函數(shù)進(jìn)行調(diào)整,由于這些函數(shù)
40、會(huì)等窗口刷新完才返回,因此當(dāng)有大量子窗口時(shí),這個(gè)過(guò)程肯定會(huì)引起閃爍。 這時(shí)我們可以應(yīng)用 BeginDeferWindowPos(), DeferWindowPos() 和 EndDeferWindowPos() 三個(gè)函數(shù)解決。首先調(diào)用 BeginDeferWindowPos(),設(shè)定需要調(diào)整的窗口個(gè)數(shù);然后用 DeferWindowPos() 移動(dòng)窗口(并非立即移動(dòng)窗口);最后調(diào)用 EndDeferWindowPos() 一次性完成所有窗口的調(diào)整。
41、 7、拖動(dòng)和調(diào)整大小時(shí)的虛線框 當(dāng)以上方法無(wú)效或者實(shí)現(xiàn)起來(lái)過(guò)于復(fù)雜,有沒(méi)有更統(tǒng)一更簡(jiǎn)潔的方法呢?可能你曾經(jīng)注意到Windows操作系統(tǒng)有這樣一種視覺(jué)效果(右擊我的電腦-> 屬性-> 高級(jí)-> 設(shè)置 -> 視覺(jué)效果-> 自定義,去掉“拖拉時(shí)顯示窗口內(nèi)容”選項(xiàng)),當(dāng)你拖動(dòng)和調(diào)整窗口大小時(shí),并不是即時(shí)顯示窗口內(nèi)容,而是出現(xiàn)一個(gè)虛線框,當(dāng)調(diào)整結(jié)束時(shí)才一次性繪制最終界面。這時(shí)一個(gè)非常好的防
42、止閃爍的方法,我們來(lái)看看怎么實(shí)現(xiàn)這種效果。 比較復(fù)雜的方法是自己畫虛線框,響應(yīng)WM_MOVING消息畫虛線框,響應(yīng)WM_MOVE消息繪制窗口內(nèi)容,不過(guò)這個(gè)方法的難度可想而知,具體內(nèi)容可以查看這個(gè)討論帖 有沒(méi)有簡(jiǎn)單的方法呢?調(diào)用 SystemParametersInfo 這個(gè)API函數(shù)可以改變系統(tǒng)“拖拉時(shí)顯示窗口內(nèi)容”項(xiàng)的設(shè)置,但是如果我們?cè)O(shè)置以后,系統(tǒng)其他窗口的行為也將被改變。其實(shí)我們只要判斷什么時(shí)候需要繪制虛線框,此時(shí)調(diào)用SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, FALSE, NULL, SPIF_SENDWININICHANGE),然后在拖動(dòng)完畢需要繪制的時(shí)候調(diào)用 SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, SPIF_SENDWININICHANGE) 恢復(fù)設(shè)置就可以了。當(dāng)然
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 互聯(lián)網(wǎng)自媒體賬號(hào)買賣及過(guò)戶安全保障合同
- 石油勘探區(qū)塊合作開(kāi)發(fā)投資協(xié)議
- 電商平臺(tái)綠色能源商品銷售與推廣服務(wù)協(xié)議
- 夫妻情感挽救忠誠(chéng)協(xié)議婚姻修復(fù)最后機(jī)會(huì)條款
- 生態(tài)農(nóng)業(yè)土壤改良及有機(jī)肥料施用技術(shù)合作協(xié)議
- 2025年廣東省茂名市高考地理一模試卷
- DB42-T 1986-2023 長(zhǎng)江干線湖北段船舶航行氣象風(fēng)險(xiǎn)預(yù)警等級(jí)
- 汽車發(fā)動(dòng)機(jī)構(gòu)造與拆裝 課件 任務(wù)12 散熱器的認(rèn)識(shí)與拆裝
- 研修學(xué)習(xí)心得體會(huì)模版
- 2023年人教版四年級(jí)語(yǔ)文上冊(cè)四單元測(cè)試卷及答案二
- 深圳市人才集團(tuán)筆試題庫(kù)
- 校園廣播設(shè)備維保合同
- 反詐宣傳課件小學(xué)生版
- 2024年全國(guó)職業(yè)院校技能大賽高職組(環(huán)境檢測(cè)與監(jiān)測(cè)賽項(xiàng))考試題庫(kù)(含答案)
- 舞蹈技巧培訓(xùn)課件
- 2025年形勢(shì)與政策-加快建設(shè)社會(huì)主義文化強(qiáng)國(guó)+第二講中國(guó)經(jīng)濟(jì)行穩(wěn)致遠(yuǎn)
- 汽車維修服務(wù)客戶滿意度提升流程
- 2024人教版七年級(jí)下冊(cè)生物第三單元 植物的生活 單元測(cè)試卷(含答案)
- 氣象防災(zāi)減災(zāi)知識(shí)科普
- 中國(guó)慢性冠脈綜合征患者診斷及管理指南2024版解讀
- 廣西田林八渡金礦 資源儲(chǔ)量核實(shí)報(bào)告
評(píng)論
0/150
提交評(píng)論