WM_EX_TRANSPARENT不重绘子窗口

Set*_*gie 5 c++ user-interface winapi ws-ex-transparent

WM_EX_TRANSPARENT在一些窗口上使用窗口样式,尝试使用透明度进行双缓冲.但是,我遇到了问题,因为当我InvalidateRect在父窗口上进行操作时,子窗口不会被重绘.

父窗口是否有责任迭代子窗口并让它们重新绘制自己,或者我做错了什么?如果它是父窗口的责任,我怎样才能获得父窗口无效矩形内的所有子窗口?

这是一个完全可编辑的例子,说明了我正在谈论的行为.将鼠标移到父窗口上时,可以看到该按钮消失.如果单击它,该按钮会自动重绘,但当您将鼠标移开并返回到父窗口时,该按钮会再次消失.

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND window, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE:
        return 0;

    case WM_MOUSEMOVE:
        InvalidateRect(window, NULL, true);

        return 0;

    case WM_NCDESTROY:
        PostQuitMessage(0);
        break;
    }

    return DefWindowProc(window, uMsg, wParam, lParam);
}

int PASCAL WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd) {
    WNDCLASS wc;
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hinst;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "test_window";

    RegisterClass(&wc);

    HWND wnd = CreateWindowEx(WS_EX_TRANSPARENT, "test_window", "test", WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, 50, 50, 400, 400, NULL, NULL, hinst, NULL);

    ShowWindow(wnd, SW_SHOW);

    HWND btn = CreateWindowEx(WS_EX_TRANSPARENT, "BUTTON", "button 1", WS_CHILD, 50, 50, 100, 50, wnd, NULL, hinst, NULL);

    ShowWindow(btn, SW_SHOW);

    MSG m;

    while (GetMessage(&m, NULL, 0, 0)) {
        TranslateMessage(&m);
        DispatchMessage(&m);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

最终的解决方案

是按照下面的建议做的,然后在父窗口的WM_PAINT消息中发送一个WM_PAINT给每个孩子并让孩子画到父BitBlt的双缓冲区(不要将任何东西绘制成自身)然后让父母的缓冲区(已被绘制到它本身,然后由它的每个孩子从Z顺序的底部到顶部)进入它HDC.这样可以在父级和子级中进行无闪烁绘制,并为子级提供透明度.这是我们到达的最终演示程序:

#include <Windows.h>

// the handle to the single child window
HWND child;

// parent's backbuffer so we can use it in the child
HDC bbuf = 0;

LRESULT CALLBACK WndProc(HWND window, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE:
        return 0;

    case WM_MOUSEMOVE:
        InvalidateRect(window, NULL, true);

        return 0;

    case WM_PAINT: {
        PAINTSTRUCT ps;
        POINT mpos;
        GetCursorPos(&mpos);
        ScreenToClient(window, &mpos);

        BeginPaint(window, &ps);

        // create the backbuffer once
        bbuf = bbuf ? bbuf : CreateCompatibleDC(ps.hdc);

        // hardcoded size is the same in the CreateWindowEx call
        static auto backbmp = CreateCompatibleBitmap(ps.hdc, 400, 400);

        static auto unused = SelectObject(bbuf, backbmp);

        POINT points[2] = {
            { 0, 0 },
            { mpos.x, mpos.y }
        };

        // painting into bbuf
        // give ourselves a white background so we can see the wblack line
        SelectObject(bbuf, (HBRUSH)GetStockObject(WHITE_BRUSH));
        Rectangle(bbuf, 0, 0, 400, 400);
        SelectObject(bbuf, (HBRUSH)GetStockObject(BLACK_BRUSH));
        Polyline(bbuf, points, 2);

        // get the child to paint itself into our bbuf
        SendMessage(child, WM_PAINT, 0, 0);

        // and after the child has drawn, bitblt everything onto the screen
        BitBlt(ps.hdc, 0, 0, 400, 400, bbuf, 0, 0, SRCCOPY);

        EndPaint(window, &ps);

        return 0;
    }

    case WM_ERASEBKGND:
        return 0;

    case WM_NCDESTROY:
        PostQuitMessage(0);
        break;
    }

    return DefWindowProc(window, uMsg, wParam, lParam);
}

LRESULT CALLBACK ChildWndProc(HWND window, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE:
        return 0;

    case WM_PAINT: {
        PAINTSTRUCT ps;

        BeginPaint(window, &ps);

        static auto backbuffer = CreateCompatibleDC(ps.hdc);
        static auto backbmp = CreateCompatibleBitmap(ps.hdc, 100, 50);

        static auto unused = SelectObject(backbuffer, backbmp);

        // copy the parent's stuff into our backbuffer (the parent has already drawn)
        BitBlt(backbuffer, 0, 0, 100, 50, bbuf, 50, 150, SRCCOPY);

        RECT r = { 0, 0, 50, 100 };

        // draw into our backbuffer
        SetBkMode(backbuffer, TRANSPARENT);
        SetTextColor(backbuffer, RGB(255, 0, 0));
        DrawText(backbuffer, "hello", 5, &r, DT_NOCLIP | DT_TABSTOP | DT_EXPANDTABS | DT_NOPREFIX);

        // bitblt our stuff into the parent's backbuffer
        BitBlt(bbuf, 50, 150, 100, 50, backbuffer, 0, 0, SRCCOPY);

        EndPaint(window, &ps);

        return 0;
    }

    case WM_ERASEBKGND:
        return 0;
    }

    return DefWindowProc(window, uMsg, wParam, lParam);
}

int PASCAL WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd) {
    WNDCLASS wc;
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hinst;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "test_window";

    RegisterClass(&wc);

    wc.style         = 0;
    wc.lpfnWndProc   = ChildWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hinst;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "transparent_window";

    RegisterClass(&wc);

    HWND wnd = CreateWindowEx(NULL, "test_window", "test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 50, 50, 400, 400, NULL, NULL, hinst, NULL);

    child = CreateWindowEx(NULL, "transparent_window", "", WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD, 50, 150, 100, 50, wnd, NULL, hinst, NULL);

    MSG m;

    while (GetMessage(&m, NULL, 0, 0)) {
        TranslateMessage(&m);
        DispatchMessage(&m);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

再次感谢johnathon花了这么多时间帮助我解决这个问题.

joh*_*han 2

从父窗口窗口样式中删除 WS_CLIPCHILDREN。这将允许父窗口在子窗口的实际空间上进行绘制,然后子窗口将在其绘制调用期间有效地在其上进行绘制。首先调用父窗口的绘制,然后调用所有子窗口的绘制。祝塞斯好运!