WM_EX_TRANSPARENT does not redraw child windows

I use the window style WM_EX_TRANSPARENTfor some windows, trying to do double buffering with transparency. However, I have a problem because when I do InvalidateRectin the parent window, the child windows do not redraw.

Is the parent window responsible for repeating child windows and forcing them to redraw themselves, or am I doing it wrong? If this is the responsibility of the parent window, how can I get all the child windows in an invalid parent rectangle?

Here is a fully compiled example illustrating the behavior I'm talking about. You can see that the button disappears when you move the mouse over the parent window. The button is redrawn if you click on it, but disappears again when you release it and return to the parent window.

#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;
}

Final decision

, WM_PAINT WM_PAINT ( - ), parent BitBlt ( , , Z-) HDC. ​​ . , :

#include <Windows.h>

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

// parent 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 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 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;
}

johnathon , , .

+5
1

WS_CLIPCHILDREN . , . , . !

+2

All Articles