5

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

最終的な解決策

以下の回答が示唆するように行うことでした。次に、親ウィンドウのWM_PAINTメッセージでWM_PAINT各子に a を送信し、子に親のダブルバッファにペイントさせ(それ自体には何も描画しません)、親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;
}

これを理解するのに多くの時間を費やしてくれたjohnathonに再び感謝します.

4

1 に答える 1

2

親ウィンドウのウィンドウ スタイルから WS_CLIPCHILDREN を削除します。これにより、親ウィンドウが子ウィンドウの領域をペイントできるようになり、子ウィンドウはペイント呼び出し中に効果的にそれをペイントします。親ウィンドウのペイントが最初に呼び出され、次に子ウィンドウのペイントが呼び出されます。頑張れセス!

于 2012-05-15T06:21:38.847 に答える