2013-2-5:注意!示例代碼有嚴重的GDI內存泄露!請在實際使用過程中注意解決!這代碼是我5年前寫的了,當時可能沒有注意到,我有空更新一下源碼。
本文介紹了基于WTL框架的一種換膚方法,可以實現對話框或框架窗口的換膚。
給窗口換膚的方法有很多種,原理基本相同,無非就是對非客戶區的重繪。只不過在實現手段上有很多種方式。本文介紹的是其中一種較簡單和易于理解的方式。即對某個窗口句柄進行子類化操作,使該窗口的非客戶區繪制被我們的代碼所替換,從而實現換膚操作。
下圖是我們進行換膚后的SDI窗口:

下面就實現的主要步驟做一些簡單的說明:
一、我們首先創建自己的換膚窗口類,繼承于CWindowImpl,并指定我們需要的窗體風格。例如:
typedef CWinTraits<WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX> CXuTraits;
class CXuSkinWindow : public CWindowImpl<CXuSkinWindow, CWindow, CXuTraits>
二、然后我們需要處理一些關鍵的消息,例如WM_NCPAINT、WM_ACTIVATE、WM_NCACTIVATE、WM_NCHITTEST、WM_SIZE等等。
三、我們還要提供一個方法,接收一個窗口的句柄,用于子類化換膚。例如:void EnableWindowFrame(HWND hWnd);
四、當然,我們的換膚圖片、顏色繪制都離不開GDI。在我的示例程序里,封裝了圖片繪制的類,用于進行圖片拉伸繪制、去透明背景色繪制等方法。(源碼在我的blog中另外兩篇文章中也有公布)
五、另外,圖片和顏色值都是從INI文件中讀取,這樣方便實現動態換膚。
一些提示
1、繪制窗口非客戶區主要分為四個部分:標題(需要拉伸繪制),左邊框,右邊框,底邊框。我們在非客戶區繪制消息中去分別繪制這四部分就好了。繪制的范圍需要根據當前窗口的大小來計算好。
2、Windows標準主題、XP主題以及Vista窗口在繪制上有一些區別,主要是窗口的標題欄高度、邊框寬度有些不同,需要根據情況動態判斷,以適應各種主題下的顯示。
示例工程(VC6 + WTL 7.0):Source Codes, Binary files and Theme files