我遇到的问题看似微不足道,但我找不到解决它的方法。在这里。我有一个窗口,里面有一些图形。
为了简单起见,我们假设它是一个实心的绿色矩形,填充了窗口的整个工作区。我希望这个矩形被重新绘制,并填补了整个窗口的窗口每次改变其大小。我最初做的是这个。我发布了来自WM_SIZE处理程序的WM_PAINT消息。
它的工作,但如果我移动鼠标快我看到一点未画(白色)区域周围的绿色矩形(实际上只有一个或两个方面,接近鼠标的地方)。我对这个问题的理解是,处理用户输入(鼠标)的系统线程比我的WM_PAINT消息处理程序工作得更快。这意味着当我开始绘制一个更新的矩形(它的大小来自WM_SIZE)时,鼠标实际上移动了一点,系统绘制了一个新的窗口框架,与我试图用绿色填充的不同。这将在调整大小时移动的边框旁边创建未填充区域。
当我停止调整大小时,绿色最终填充了整个窗口,但在调整大小期间,在靠近边框的地方发生了一点 Flink ,这很烦人。为了解决这个问题,我尝试了以下方法。
bool finishedPainting;
RECT windowRect;
case WM_PAINT :
// ..... painting here
finishedPainting = TRUE;
break;
case WM_SIZE :
// .... some actions
// posting WM_PAINT
InvalidateRect(hWnd, NULL, FALSE);
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
case WM_SIZING :
// this supposedly should prevent the system from passing
// new window size to WM_SIZE
if (!finishedPainting) memcpy((void*)lParam, &windowRect, sizeof(windowRect));
else {
// remember current window size for later use
memcpy(&windowRect, (void*)lParam, sizeof(windowRect));
finishedPainting = FALSE;
}
return TRUE;
它不工作。作为一个小小的变化,我也尝试了这个。
bool finishedPainting;
POINT cursorPos;
case WM_PAINT :
// ..... painting here
finishedPainting = TRUE;
break;
case WM_SIZE :
if (!finishedPainting) SetCursorPos(cursorPos.x, cursorPos.y);
else {
finishedPainting = FALSE;
GetCursorPos(&cursorPos);
// .... some actions
InvalidateRect(hWnd, NULL, FALSE);
PostMessage(hWnd, WM_PAINT, 0, 0);
}
break;
这个也不行。据我所知,这个问题的解决方案在于以某种方式放慢鼠标速度,使其在绘画完成后才移动到屏幕上的下一个位置(拖动窗口的角落或侧面)。
有什么想法可以实现这一点吗?或者也许是我看待问题的方式有什么根本性的错误,解决方案在其他地方?
//====================================
更新
我做了一些实验,这是我的发现
1)当调整大小时,消息的顺序是WM_SIZING - WM_NCPAINT - WM_SIZE - WM_PAINT。这看起来有点奇怪。我希望WM_SIZE跟随WM_SIZING,而不会被WM_NCPAINT中断
2)在每个消息处理程序中,我在调整大小时检查窗口的宽度(为了简单起见,我只改变宽度)。令人惊讶的是,WM_SIZE中测量的宽度与WM_SIZING中的宽度不同,但与WM_NCPAINT和WM_PAINT中的宽度相同。这不是问题本身,只是一个奇怪的事实。
3)我得出的结论是,有两个主要原因 Flink 发生在窗口边界附近。第一个是WM_NCPAINT在WM_PAINT之前。想象一下,你正在打开你的Windows。新帧将首先出现(WM_NCPAINT先出现),然后WM_PAINT填充客户区。当新帧已经在屏幕上时,人眼捕捉到这一短时间段,但它是空的。即使您指定不希望窗口背景在重新绘制之前被删除,新添加的区域仍然是空的,您可以在一瞬间看到它。当您抓住窗口的右边缘并将其快速向右移动时, Flink 的原因得到了最好的证明。 Flink 效果的另一个原因是不太明显,当你抓住窗口的左边缘并将其向左移动时,效果最好。在此移动过程中,您将看到沿着右边缘的未填充区域。据我所知,这种影响是由它引起的。当用户调整Windows大小时,将执行以下操作:A)它发送WM_NCPAINT来绘制新帧,B)它将旧客户区的内容复制到新窗口的左上角(在我们的情况下,它移动到左边),C)它发送WM_PAINT来填充新客户区。然而,在阶段B期间,由于某种原因,Windows沿着右边缘产生那些未填充的区域,尽管它似乎不应该,因为旧内容应该留在原地,直到它在WM_PAINT期间被重新绘制。
好吧,问题仍然存在-如何在调整大小时消除这些伪影。据我所知,现在使用标准技术和函数是不可能的,因为它们是由Windows在调整大小时执行的步骤序列引起的。交换WM_NCPAINT和WM_PAINT可能会有所帮助,但这似乎超出了我们的控制范围(除非有一种简单的方法来做到这一点,我只是不知道)。
4条答案
按热度按时间6gpjuf901#
您不应该自己发布或发送WM_PAINT消息。相反,用途::InvalidateRect使窗口的某些部分无效,并让Windows决定何时发送WM_PAINT消息。
weylhg0b2#
Windows是故意这样工作的。它通常被认为是更重要的是响应用户(即鼠标)而不是拥有一个完全最新的绘制窗口。
如果您总是在WM_PAINT处理程序中绘制整个窗口,则可以通过重写WM_ERASEBKGND处理程序并返回而不做任何操作来消除大量 Flink 。
如果你真的坚持窗口更新优于鼠标响应,用一个使用RDW_UPDATENOW标志的RedrawWindow调用替换InvalidateRect调用。
答:你的观察是有意义的。WM_SIZING出现在窗口调整大小之前,让你有机会修改大小和/或位置。您可以尝试在WM_NCPAINT处理程序中绘制工作区,甚至在绘制边框之前。绘制完后,可以验证工作区以防止再次绘制。
ffscu2ro3#
手动发布WM_PAINT或WM_SIZE是个坏主意。一个奇怪的黑客疯狂的事情,你可以做的,是记录在一个
RECT
在WM_SIZE,使用MoveWindow
改变窗口的大小 * 回 * 它的前一个,然后手动调整它 * 再次 * 使用MoveWindow
在WM_PAINT消息 * 后 * 你做的画。另一个可能的解决方案是不断地用颜色填充你的窗口,不管屏幕是否调整了大小。即
当然,你也可以将窗口的背景色设置为绿色,而不是自己绘制:
iq3niunx4#
我也有同样的问题。有很多词来描述我的解决方案,所以总结一下,我只是改变了窗口类的样式,使其具有CS_VREDRAW| CS_HREDRAW: