天天看點

VC++界面程式設計之--使用分層視窗實作界面皮膚

VC++界面程式設計之--使用分層視窗實作界面皮膚

使用分層界面來實作界面皮膚的好處是:可以保證圖檔邊緣處理不失真,且能用于異形視窗上,如一些不規則的視窗,你很難用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。

凡事皆有取舍,一切都根據實際需求做出取舍。正如分層視窗一樣,喪失了一些正常視窗的普通函數操作能力,但換來了界面皮膚的完美顯示。