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上下载开源工程了解。