![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TUaVXOtNGaSNzY1ZVbjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DO2QTNwkDM5AzNwATMzEDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
使用分層界面來實作界面皮膚的好處是:可以保證圖檔邊緣處理不失真,且能用于異形視窗上,如一些不規則的視窗,你很難用SetWindowRgn來達到理想效果。
在很多情況下,界面的漂亮與否,取決于PS的制作及創意,而界面程式設計所需要做的就是将圖檔完整無缺的展示給使用者。
分層視窗的好處就是:用一層視窗來描繪視窗背景,以確定背景圖檔不會失真,而另一層視窗用來放置控件,除控件之外的區域全是透明的。為什麼要這麼麻煩呢?因為我們将要用到UpdateLayeredWindow來實作視窗背景描繪,一旦執行這個函數後,視窗的控件将會被覆寫,不會再顯示。是以我們要再加一層透明視窗,來放置控件,以確定控件不會受到UpdateLayeredWindow的影響。
這樣一來,視窗繪圖将不會影響到控件,并且可以達到視窗透明,控件不透明的效果。當然這個隻是個别的視窗實作效果需求,這裡不做重點讨論。
這裡我用了兩個視窗類來完成此項工作,CThemedLayerWnd和CThemedAlphaWnd。CThemedLayerWnd負責截取正常視窗的建立消息,并在正常視窗建立之前,建立CThemedAlphaWnd;CThemedAlphaWnd主要負責視窗的描繪,也就是使用UpdateLayeredWindow來讓Windows自己描繪視窗皮膚。
正常視窗隻需要繼承模闆類CThemedLayerWnd,并将CThemedLayerWnd加入到自己的視窗消息鍊中即可:
class CMainDlg : public CThemedLayerWnd<CMainDlg>,
public CUpdateUI<CMainDlg>,
public CMessageFilter,
public CIdleHandler
{
BEGIN_MSG_MAP(CMainDlg)
CHAIN_MSG_MAP(CThemedLayerWnd<CMainDlg>)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
};
在正常視窗執行Create函數時,提前建立CThemedAlphaWnd,這樣一來,CThemedAlphaWnd就是CThemedLayerWnd的父視窗,CThemedLayerWnd理所當然成為子視窗,并自動将視窗位置置于CThemedAlphaWnd之上,這也正是我們想要的效果。
// Create normal dialog and alpha window.
HWND Create(const HWND wndParent, const int nImageID)
// Create alpha window before normal dialog created.
// Do not use WS_CHILD style, we need separated alpha window.
m_wndAlpha = new CThemedAlphaWnd;
m_wndAlpha->Create(wndParent, CRect(0, 0, 100, 100), NULL, WS_VISIBLE);
ATLASSERT(m_wndAlpha->m_hWnd != NULL);
SetBackgroundImage(nImageID);
// Create normal dialog.
HWND hwnd = CDialogImpl<T>::Create(m_wndAlpha->m_hWnd);
// Pass normal window handle.
m_wndAlpha->SetNormalWnd(m_hWnd);
// Set normal dialog style.
UpdateWindowStyle();
return hwnd;
}
這裡還有個關鍵的問題,視窗上面是有控件的,如果控件要擷取父視窗的背景畫刷該怎麼辦?而CThemedLayerWnd是透明的,你是沒辦法擷取到透明背景的。是以需要截取CThemedLayerWnd的WM_PAINT消息,當控件想要通過WM_PAINT來擷取視窗背景時,将視窗皮膚圖檔傳遞給控件。
// Only for control get background image.
void DoPaint(CDCHandle dc)
if (m_bmpBack.m_hBitmap != NULL)
{
HBITMAP hBmp = dc.SelectBitmap(m_bmpBack.m_hBitmap);
ATLASSERT(hBmp != NULL);
}
分層視窗帶來的不便之處就是:你對視窗的操作都是介于兩個視窗之間的,是以你需要在此上面多花費一些功夫,比如移動視窗,實際是移動兩個視窗。再比如正常視窗調用CenterWindow,實際是讓CThemedLayerWnd來移動視窗,你不得不重寫CenterWindow來先移動CThemedAlphaWnd然後再來移動CThemedLayerWnd。
凡事皆有取舍,一切都根據實際需求做出取舍。正如分層視窗一樣,喪失了一些正常視窗的普通函數操作能力,但換來了界面皮膚的完美顯示。