1. 程式人生 > >CStatic子類化解決背景透明,文字重疊,重新整理閃爍問題

CStatic子類化解決背景透明,文字重疊,重新整理閃爍問題

由於是很少使用VS2008的MFC,遇到了很多麻煩,其實都是很初級的;但是我還是想做點記錄,以示對自己工作的鼓勵,同時也是留個紀念,因為隨著微軟政策的變化,以後MFC的程式將不會是趨勢了,而我也有意從事Android平臺的開發,所以不太可能做MFC程式了。

需求是這樣的,我用一個CStatic控制元件顯示遠端傳送來的文字資訊,由於是不斷獲取,所以會不斷重新整理,程式主介面是對話方塊,對話方塊的背景是一張以藍色為主色調的BMP圖片,我希望CStatic控制元件能夠透明顯示文字,而不是一個白色不透明方框。

    在解決的過程中,出現了,背景透明後,有文字重疊問題;解決文字重疊問題,卻帶出了介面重新整理閃爍問題。因為繪圖是需要進行一系列影象處理的,一擦一繪會造成影象顏色反差,WM_PAINT訊息的頻繁響應,勢必造成重繪過於頻繁,必然形成嚴重的閃爍。

    因此我對MFC視窗繪圖框架,做了簡單瞭解,對視窗繪製和重新繪製過程中的OnPaint,OnEraseBkgnd,OnCtrColor,InvalidateRect,UpdateWindow有了一個大致上的認識。網上有很多資料,這裡不介紹了。

    對於背景重繪一般是把實現程式碼放在OnEraseBkgnd或者OnPaint中來實現的。如果放在OnPaint中,需要同時過載OnEraseBkgnd函式並且不新增任何程式碼直接return TRUE;

1.使用WM_CTLCOLOR訊息

首先我想到的是MFC的控制元件應該有一個直接設定背景透明的API,很遺憾CStatic在VS2008的MFC的版本沒提供這樣的API。

所以我選擇了在網上比較多的一種做法,在主對話方塊使用WM_CTLCOLOR訊息,並重載訊息響應函式OnCtlColor,在該函式中設定控制元件的文字顏色,背景透明,並返回一個空畫刷,以實現透明效果。


結果可想而知,靜態和動態文字都實現了透明,但動態重新整理文字時出現了文字重疊,原因是OnCtlColor中返回了空畫刷,相當於沒有重新整理,直接重繪的。

為了解決重新整理的問題,我使用了,如下兩種方式:

1.  將控制元件隱藏然後再顯示,以達到強制重繪的目的,實現程式碼如下圖:


m_ticket_type.是一個CStatic的物件,關聯了一個控制元件IDC_TICKET_TYPE,結果是,背景是透明的沒問題,文字也沒有重疊了,但我始終覺得這種方式比較消耗資源,而且介面出現了嚴重的閃爍。

1.  呼叫相關係統API,強制重新整理控制元件區域,實現程式碼如下圖:


先獲取控制元件的子區域矩形,然後呼叫InvalidateRect函式強制重新整理,該函式會丟擲WM_PAINT訊息,由OnPaint中的程式碼實現重新整理重繪。這段程式碼是在主對話方塊中完成的,即一般大家所說的,通過父視窗強制重新整理控制元件區域。同樣的,這個方法也解決了背景透明問題,文字重疊問題,但是還是有嚴重的閃爍,而且跟區域大小沒有關係,所以我懷疑跟裝置效能關係不大,因此放棄了在此基礎上新增雙緩衝方式繪圖的想法。而且這種在父視窗通過響應系統訊息,重新整理子控制元件的做法,也不符合面向物件封裝的特性,因此我放棄了這種方式。

2.子類化CStatic

    要解決上述問題,同時達到面向物件要求,只有通過繼承CStatic類,並在子類中實現背景透明,字型設定,文字顏色設定等功能。這方面可以自己寫,也可以使用開原始碼,在www.codeproject.com網站上提供了很多功能比較完善,封裝也比較好的的開源類,可以免費使用,不需要下載積分,可以使用國內的網易郵箱註冊賬號。需要注意的是,這些類往往只是提供思路的,要實現自己特殊功能,還得自己新增成員函式,不過人家已經搭好框架了,添加個功能就方便多了。

    需要強調的是MFC在子類中新增WM_CTLCOLOR訊息處理函式OnCtrColor是不會被響應的,MFC4.0以後提供了一個訊息反射的機制,在VS2008中新增訊息時訊息名稱前面包含“=”(等號)的即是支援訊息反射的,=WN_CTLCOLOR訊息,新增到程式碼中是ON_WM_CTLCOLOR_REFLECT(),響應函式是CtlColor。

2.1 對CLabel開源類的使用

    該類是一個封裝的挺漂亮的一個類,將其新增到我的工程,可以直接宣告物件使用,各種功能也僅僅是呼叫成員函式,很方便。該類是在OnPaint中實現重繪的,使用了雙緩衝,如下所示,當控制元件設定為透明時,記憶體DC是直接獲取的OnPaint中的dc。


不過我不明白的是,pDCMem通過DrawText設定文字內容後,當控制元件設定為背景透明時,並沒有使用BitBlt將記憶體DC拷貝繪製的過程,但在介面上我仍然能夠看到新設定的文字,也許是我對程式碼的理解還不夠。


如前所述OnEraseBkgnd函式在OnPaint重繪背景的情況下,直接返回TRUE,原因是OnPaint實際上回預設呼叫OnEraseBkgnd的,如果不過載OnEraseBkgnd,該函式是預設繪製白色背景的矩形框,這將造成明顯的閃爍。


通過下面的方式能夠實現透明,意思是設定“視窗”為透明


2.2 對CLabel開源類的改造

儘管該類封裝的很漂亮,功能也比較完善;然而,我經過多次測試發現,當需要動態重新整理CStatic控制元件是,仍然有很明顯的閃爍。我分析還是由於多次重新整理的問題,SetTransparent也許只是設定視窗的一個狀態,必須返回一個空畫刷,才能避免繪製一個白色框,造成閃爍。下面採用訊息反射,過載ON_WM_CTLCOLOR_REFLECT訊息響應函式CtlColor

新增該訊息響應的方法如下:

    1.在類檢視中選中該類名


2.滑鼠右擊選擇屬性開啟。


3.在彈出的“屬性”視窗中點選紅色方框按鈕,選擇=WM_CTLCOLOR訊息,在藍色方框的下拉選單中選擇新增CtlColor


4.在CtlColor訊息響應函式中新增如下程式碼,並註釋return NULL


改造過後的CLabel物件能夠很好的支援控制元件背景透明,文字重疊,重新整理閃爍問題;但是該方法我並沒有做效能測試,反正目標機器完全不需要考慮這個問題。

2.3 對CTransparentStatic開源類的使用

該類也是在CodeProject上分享的一個繼承類,它不需要改造,能夠完美解決我的問題,它採用在OnEraseBkgnd裡重繪介面的方式,使用雙緩衝繪圖技術,同時過載CtlColor函式實現透明


遺憾的是,該類只提供透明等問題的解決方案,功能太少,至少連字型和文字顏色設定都沒有,因此我為它添加了三個成員函式以實現我的需求,直接使用即可,很簡單就不註釋了。


我在CSDN上傳了對於這兩個改造類的演示的DEMO,最新版本有些改動,跟這篇文件有些不同,不過並不影響

需要說明的是這個DEMO在Debug模式下會出現一個Attach斷言錯誤,這是我自己造成的,由於不能刪除上傳的資源,所以我單獨寫了一篇勘誤部落格文章

這是我的小白用MFC的工作日誌的一部分,感興趣的話,我在豆丁有個word的完整版,歡迎下載,免費的

由於是第一次在csdn寫部落格,如有錯別字,版式太亂,請多包涵!!