2012-05-14 8 views
5

Estoy usando el estilo de ventana WM_EX_TRANSPARENT en algunas ventanas para intentar hacer doble almacenamiento en el búfer con transparencia. Sin embargo, tengo un problema porque cuando hago InvalidateRect en la ventana principal, las ventanas secundarias no se vuelven a dibujar.WM_EX_TRANSPARENT no vuelve a pintar las ventanas secundarias

¿Es la ventana principal la responsabilidad de iterar las ventanas secundarias y hacer que se vuelvan a pintar, o lo estoy haciendo mal? Si es responsabilidad de la ventana principal, ¿cómo puedo obtener todas las ventanas secundarias dentro del rectángulo no válido del elemento principal?

Aquí hay un ejemplo completamente compilable que ilustra el comportamiento del que estoy hablando. Puede ver que el botón desaparece cuando mueve el mouse sobre la ventana principal. El botón se vuelve a dibujar si hace clic en él, pero desaparece de nuevo cuando deja de usarlo y regresa a la ventana principal.

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

La solución final

era hacer que la respuesta a continuación sugerido, y luego en el mensaje de la ventana padre WM_PAINT enviar un WM_PAINT a cada niño y que los niños pintura en doublebuffer de los padres (no pintar nada en sí mismo) y luego tener el padre BitBlt es el búfer (que ha sido dibujado por sí mismo y luego por cada uno de sus hijos que van desde la parte inferior a la parte superior del orden Z) en su HDC. Eso permite el dibujo sin parpadeo en el padre y el niño, y la transparencia para el niño. Este es el programa de demostración definitiva llegamos a:

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

Gracias de nuevo a Johnathon por pasar tanto tiempo ayudarme a resolver esto.

+0

http://stackoverflow.com/questions/1842377/double-buffer-common-controls – Flot2011

+0

@ Flot2011 'WS_EX_COMPOSITED' hace su propio doble buffer y yo quiero hacer el mío. Su uso evita que las ventanas hijas desaparezcan, pero parpadean realmente mal. Puedes probarlo con mi programa de ejemplo, hace lo mismo. –

+0

Lástima que su solución no funcione en los controles estándar. No puedo evitar sentir que hay una mejor solución, pero no la tengo. –

Respuesta

2

elimina WS_CLIPCHILDREN del estilo de ventana principal de Windows. Esto permitirá que la ventana principal pintee sobre el espacio de la ventana secundaria, luego la ventana secundaria pintará de forma efectiva durante su llamada de pintura. Primero se llama a la pintura de la ventana principal, luego se llama a todas las ventanas secundarias. Buena suerte Seth!

+0

Gracias de nuevo, he estado trabajando en este estúpido problema por días. –

+0

No hay problema Seth. Me alegro de poder ayudarte. – johnathon

Cuestiones relacionadas