Windows半透明窗口開發技巧

www.visual-gear.com 原創技術文章

在windows平台上從窗口繪圖有兩種方法:

  1. 第一種響應窗口的WM_PAINT消息,使用窗口DC進行繪制

  2. 第二種是將窗口樣式設置為層窗口,即 WS_EX_LAYERED
    設置為該樣式之后窗口將不會產生任何的WM_PAINT消息,我們通過GetDC等方法在DC上繪圖也不會有任何的效果。
    我們只能通過UpdateLayeredWindow這個API將我們需要顯示的內容提交給窗口。

窗口的內容顯示將全部交給用戶進行處理,任何的窗口內容改變我們都需要重建位圖,並調用該API進行顯示內容提交。

默認的非層窗口是無法讓窗口半透明顯示,因為繪制到最后都會和黑色進行混合填充到DC上。
而層窗口則允許我們可以使用半透明位圖顯示窗口內容,這樣我們就可以實現半透明窗口效果。

具體代碼如下:

首先設置窗口樣式為層窗口

    LONG_PTR dwExStyle = GetWindowLongPtr((HWND)_handle, GWL_EXSTYLE);
    if ((dwExStyle & WS_EX_LAYERED) != WS_EX_LAYERED)
    {
        SetWindowLongPtr((HWND)_handle, GWL_EXSTYLE, dwExStyle | WS_EX_LAYERED);
    }

 

首先建立一個內存位圖和內存DC

    HBITMAP CreateGDIBitmap(int nWid, int nHei, void ** ppBits)
    {
        BITMAPINFO bmi;
        memset(&bmi, 0, sizeof(bmi));
        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth = nWid;
        bmi.bmiHeader.biHeight = -nHei;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = BI_RGB;
        bmi.bmiHeader.biSizeImage = 0;

        HDC hdc = GetDC(NULL);
        LPVOID pBits = NULL;
        HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, ppBits, 0, 0);
        ReleaseDC(NULL, hdc);
        return hBmp;
    }
...
    _hBmp = CreateGDIBitmap(_w, _h, &_pBmpBits);
    _hMemDc = CreateCompatibleDC(hdc);
    SelectObject(_hMemDc, _hBmp);

 

此時我們可以在這個內容DC上做任何我們想要的繪制,由於這張位圖是一個32位位圖所以帶有透明通道。
當我們完成繪制之后我們通過UpdateLayeredWindow就可以提交顯示內容到窗口上了。

            HDC hdc = ::GetDC(_hwnd);
            RECT rcClient;
            GetWindowRect(_hwnd, &rcClient);

            POINT ptDest = { rcClient.left, rcClient.top };
            POINT ptSrc = { 0, 0 };
            SIZE szLayered = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top };
            BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };

            ::UpdateLayeredWindow(_hwnd, hdc, &ptDest, &szLayered, _hMemDc, &ptSrc, (COLORREF)0, &bf, ULW_ALPHA);
            ::ReleaseDC(_hwnd, hdc);

 

需要注意的是:

  1. 層窗口不會影響WM_PAINT消息以外的任何消息,鼠標鍵盤消息仍然可以使用。
  2. 由於層窗口沒有了的WM_PAINT消息,所有窗口上的任何子窗口控件都不會進行顯示,如按鈕,復選框等等。
  3. 層窗口顯示性能要低於非層窗口,因為帶上透明通道的處理過程。
  4. 每次窗口內容的改變都需要重新提交UpdateLayeredWindow到窗口上。
  5. 層窗口的顯示內容不能超過窗口的原始大小,超出部分窗口會自動進行裁剪。

相關示例代碼可登陸www.visual-gear.com上下載開源工程了解。