2012-05-14 47 views
5

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

它是父窗口的责任迭代子窗口,让他们重绘自己,或者我做错了吗?如果是父窗口的责任,我如何获得父窗口无效矩形内的所有子窗口?

下面是一个完全可编译的例子,说明我正在谈论的行为。将鼠标移到父窗口上时,可以看到该按钮消失。如果您点击该按钮,该按钮将自动重绘,但当您将其从鼠标移开并重新显示到父窗口时,该按钮会再次消失。

#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每个孩子,让学生做的答案画到父的双缓冲(而不是自己画任何东西),然后让父母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; 
} 

感谢乔纳森花了这么多时间帮我想出解决办法。

+0

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

+0

@ Flot2011'WS_EX_COMPOSITED'做自己的双缓冲,我想做我自己的。使用它可以防止子窗口消失,但它们的闪烁非常糟糕。你可以用我的示例程序来尝试它,它也会做同样的事情。 –

+0

太糟糕了,您的解决方案无法在标准控件上使用。我不禁感到有更好的解决方案,但我没有提供。 –

回答

2

从您的父窗口窗口样式中删除WS_CLIPCHILDREN。这将允许父窗口在子窗口的房地产上绘画,然后子窗口将在绘画调用期间有效地绘制该窗口。父窗口的paint被首先调用,然后任何子窗口调用它们的paint。祝你好运赛斯!

+0

再次感谢,我一直在这个愚蠢的问题上工作了几天。 –

+0

Seth没有问题。很高兴我能帮助你。 – johnathon