我读到过,我不应该手动发送WM_PAINT
,而应该调用InvalidateRect
,但是没有发现任何原因。为什么不呢?
update对InvalidateRect
有效,但对SendMessage(WM_PAINT)
无效
LRESULT CALLBACK window_proc(HWND wnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
switch (msg)
{
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(wnd, &ps);
Polyline(..);
EndPaint(wnd, &ps);
return 0;
case WM_USER:
// SendMessage(wnd, WM_PAINT, NULL, NULL);
// InvalidateRect(wnd, NULL, FALSE);
return 0;
}
}
WM_PAINT
的官方文档在备注部分的第一句话就指出不应该这样做。说真的,这应该是一个足够的理由不。
至于技术原因,我想这是其中之一,摘自BeginPaint
评论部分:
更新区域由InvalidateRect或InvalidateRgn函数设置,并由系统在调整大小、移动、创建、滚动或任何其他影响客户端区域的操作后设置。
因此,如果您手动发送WM_PAINT
, BeginPaint
可能无法正常工作。
可能有更多的原因/惊喜
如果您想立即触发重新绘制,正确的方法是:
-
使用
InvalidateRect()
+UpdateWindow()
-
使用
RedrawWindow()
这些将触发新的WM_PAINT
消息生成
您没有任何关于其他程序的窗口的信息。只有操作系统有这个信息。所以你真的不知道你的窗户何时何地需要重新粉刷。WM_PAINT和BeginPaint提供了这些缺失的信息
因为WM_PAINT
不是真正的消息
想象一下,每个窗口都有一个存储"无效区域"的结构,也就是说,"屏幕上窗口的这一部分不再是最新的,需要重新绘制"。
无效区域由窗口管理器自己修改(窗口大小调整,覆盖等),或通过调用InvalidateRect
, ValidateRect
, EndPaint
等。
现在,这里是GetMessage
如何处理这个的粗略模型:
... GetMessage(MSG* msg, ...)
{
while(true) {
if(ThereIsAnyMessageInTheMessageQueue()) {
*msg = GetFirstMessageOfTheMessageQueue();
return ...;
} else if(TheInvalidRegionIsNotEmpty()) {
*msg = CreateWMPaintMessage();
return ...;
} else {
WaitUntilSomethingHappend();
}
}
}
tl;dr: WM_PAINT
表示接收,而不是发送。