【发布时间】: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->HDC,也应该调用它以响应WM_PAINT。
但是如果BeginPaint()确实为子窗口触发WM_PAINT,那么我的日志调用顺序应该看起来像WINDOW_PAINT_START -> BUTTON_PAINT_START -> BUTTON_PAINT_END -> WINDOW_PAINT_END,实际上看起来像WINDOW_PAINT_START -> WINDOW_PAINT_END -> BUTTON_PAINT_START -> 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” - 我之前的评论解释了这不会发生以及为什么不会发生。跨度>