我遇到的问题看似微不足道,但我找不到解决的方法。这里是。我有一个窗口,里面有一些图形。
为了简单起见,我们假设它是一个实心绿色矩形,填充了窗口的整个客户区域。我希望每次窗口改变大小时都重新绘制这个矩形并填充整个窗口。我最初做的就是这样的。我已经发布WM_PAINT消息来自WM_SIZE处理程序。
它有效,但如果我快速移动鼠标,我会在绿色矩形周围看到一些未绘制的(白色)区域(实际上只有一侧或两侧,靠近鼠标所在的位置)。我对这个问题的理解是,处理用户输入(鼠标)的系统线程比我的 WM_PAINT 消息处理程序运行得更快。这意味着当我开始绘制更新的矩形(其大小取自 WM_SIZE)时,鼠标实际上移动了一点,并且系统绘制了一个新的窗口框架,该窗口框架与我尝试用绿色填充的窗口框架不同。这会在边框旁边创建未填充的区域,这些区域在调整大小时会移动。
当我停止调整大小时,绿色最终会填充整个窗口,但在调整大小期间,边界附近会发生一些闪烁,这很烦人。为了解决这个问题,我尝试了以下方法。
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;
这也是行不通的。据我了解,问题的解决方案在于以某种方式减慢鼠标速度,以便仅在绘画完成后鼠标才移动到屏幕上的下一个位置(用鼠标拖动窗口的角落或一侧)。
有什么想法如何实现这一目标?或者也许我看待问题的方式有根本性的错误,解决方案在其他地方?
// ================================================== ====
Update
我做了一些实验,这是我发现的
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)我得出的结论是,窗口边框附近发生闪烁的主要原因有两个。第一个是 WM_NCPAINT 出现在 WM_PAINT 之前。想象一下您正在拉伸窗户。新框架将首先出现(WM_NCPAINT 首先出现),然后 WM_PAINT 填充客户区。当新帧已经出现在屏幕上但它是空的时,人眼会捕捉到这一短暂的时间。即使您指定不希望在重新绘制之前删除窗口背景,新添加的区域仍然是空的,您可以立即看到它。当您抓住右侧窗口边缘并将其快速向右移动时,可以最好地证明闪烁的原因。闪烁效果的另一个原因不太明显,当您抓住左窗口边缘并将其向左移动时,最容易看到。在此移动过程中,您将看到沿右边缘的未填充区域。据我了解,效果就是由此造成的。当用户调整大小时,Windows 会执行以下操作:A)发送 WM_NCPAINT 来绘制新框架,B)将旧客户区的内容复制到新的窗口左上角(在我们的例子中,它移动到左侧), C) 它发送WM_PAINT来填充新的客户区。然而,在 B 阶段,由于某种原因,Windows 会沿着右边缘生成那些未填充的区域,尽管看起来不应该,因为旧内容应该保留在原来的位置,直到在 WM_PAINT 期间重新绘制。
好吧,问题仍然是 - 如何在调整大小期间消除这些伪影。据我现在所知,使用标准技术和函数是不可能的,因为它们是由 Windows 在调整大小期间执行的步骤顺序引起的。交换 WM_NCPAINT 和 WM_PAINT 可能会有所帮助,但这似乎超出了我们的控制范围(除非有一种我不知道的简单方法可以做到这一点)。