【问题标题】:Due to invalid ValidateRgn() childs windows do not get WM_PAINT messages由于无效的 ValidateRgn() 子窗口没有收到 WM_PAINT 消息
【发布时间】:2020-10-22 21:48:54
【问题描述】:

我有带有Button 控件的窗口,我使用Direct2D 进行绘图,重要的WindowProc 片段:

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
    case WM_CREATE:
        HWND button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 200, 100, hWnd, CONTROL_ID, desc->hInstance, 0);
        break;

    case WM_PAINT:
        render_target->BeginDraw();
        ... rendering stuff ...
        HRESULT result = render_target->EndDraw();

        // Validate region:
        ValidateRgn(hWnd, nullptr); // validate entire client area
        break;
...
}

这段代码不太行,我的孩子Button 没有画。我想它没有收到WM_PAINT 消息,因为我验证了整个窗口,因此窗口不会要求重新绘制相同的像素两次。

所以我考虑到这一点,并从验证中排除 Button 区域:

    // Validate region:
    HRGN update = CreateRectRgn(0, 0, 0, 0);
    HRGN button = CreateRectRgn(10, 10, 210, 110);

    GetUpdateRgn(hWnd, update, false); // get update rect
    CombineRgn(update, update, tab, RGN_DIFF); // exclude button region
    ValidateRgn(hWnd, update); // validate window client area (excluding button) to stop receiving WM_MESSAGE

现在Button 仍未绘制,而且我在无限循环中收到WM_PAINT,更新区域等于Rgn(10, 10, 210, 110)。我预计在WindowProc 中的WM_PAINT 之后,子Button 应该收到它,并且将验证缺少的更新区域。


如果我将WM_PAINT 消息包装在BeginPaint(hWnd, nullptr) EndPaint(hWnd, nullptr) 中,一切正常:

    case WM_PAINT:
        ::log << "WINDOW_PAINT_START" << std::endl;
        BeginPaint(hWnd, nullptr); // use BeginPaint from GDI

        render_target->BeginDraw();
        ... rendering stuff ...
        HRESULT result = render_target->EndDraw();

        // Validate region code is not needed anymore because BeginPaint take care of that.
        EndPaint(hWnd, nullptr);
        ::log << "WINDOW_PAINT_END" << std::endl;
        break;

似乎BeginPaint() 验证更新区域并为子窗口触发WM_PAINT。即使您不使用GDI 并且它是PAINTSTRUCT-&gt;HDC,也应该调用它以响应WM_PAINT。 但是如果BeginPaint()确实为子窗口触发WM_PAINT,那么我的日志调用顺序应该看起来像WINDOW_PAINT_START -&gt; BUTTON_PAINT_START -&gt; BUTTON_PAINT_END -&gt; WINDOW_PAINT_END,实际上看起来像WINDOW_PAINT_START -&gt; WINDOW_PAINT_END -&gt; BUTTON_PAINT_START -&gt; BUTTON_PAINT_END。所以WM_PAINT for Button 被延迟到WinProc 从它的WM_PAINT 返回:因此触发发生在外面,因此孩子们在主窗口绘制之后绘制,这符合MSDN。

那么WM_PAINT 到底是在哪个时刻发送到子窗口的?

【问题讨论】:

  • 你有WS_CLIPCHILDREN 样式集吗?
  • 没有。 HWND window = CreateWindowExW(0, window_unique_name, window_title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, window_size.width, window_size.height, nullptr, nullptr, hInstance, nullptr);
  • WM_PAINT 仅在调用消息检索函数时生成(请参阅Even though mouse-move, paint, and timer messages are generated on demand, it’s still possible for one to end up in your queue)。你永远不会看到嵌套的WM_PAINT 消息。
  • @IInspectable 好的,但我发现它与我的问题无关。
  • “如果 BeginPaint() 确实为子窗口触发 WM_PAINT - 我之前的评论解释了这不会发生以及为什么不会发生。跨度>

标签: c++ winapi gdi direct2d


【解决方案1】:

MSDN 来帮忙Child Window Update Region:

每个子窗口都有一个更新区域,(...) 设置子窗口时系统不会设置父窗口的更新区域。应用程序无法通过使子窗口无效来为父窗口生成 WM_PAINT 消息。 同样,应用程序无法通过使完全位于子窗口下方的父客户区的一部分无效来为子窗口生成 WM_PAINT 消息。在这种情况下,两个窗口都不会收到 WM_PAINT 消息。

关于无限循环之谜:在代码中,您仅对 MainWindow 验证/无效(操作)区域,因此在 WindowProc 返回后 -> 队列为空 -> 区域不明确 -> WM_PAINT 正在发送到窗口。

如果要为子窗口生成WM_MESSAGE,则使子窗口更新区域无效。请记住,原点 (0,0) 已更改为相对于 更新区域,它不再是主(父)窗口更新区域:

    HRGN wrong = CreateRectRgn(10, 10, 210, 110); // wrong, will produce GUI artifacts, try and see for yourself (only part if any of button will be painted).
    HRGN good = CreateRectRgn(0, 0, 200, 100); // good, basically it always be Rgn(0,0 width,height) values in region.
    InvalidateRgn(>>>button<<<, good, false); // invalide child window, first param point to button HWND.

什么时候会出现这个对孩子的失效区域?

MSDN 在这件事上可能会产生误导(但也许只是你理解不同),再次Child Window Update Region

子窗口的更新和可见区域受子窗口的父窗口影响;这不适用于其他样式的窗户。 系统往往在设置父窗口的更新区域的同时设置子窗口的更新区域,导致子窗口在父窗口收到WM_PAINT消息时也收到了。

但实际上,当您为主窗口调用InvalidateRgn(hWnd, ..., ...) 时,子窗口的这种失效不会发生,而是响应WM_PAINT,更准确地说,BeginPaint(hWnd, ...) 在内部对区域进行一些计算并为子窗口调度失效(但我不确定,这只是我的观察)。

【讨论】:

    猜你喜欢
    • 2022-11-12
    • 1970-01-01
    • 2021-01-29
    • 2010-11-01
    • 2012-06-30
    • 1970-01-01
    • 2014-05-27
    • 2013-02-24
    • 2010-10-30
    相关资源
    最近更新 更多