我遇到的问题似乎很琐碎,但我找不到解决方法。我的窗口里有一些图形。
为了简单起见,我们假设它是一个填满整个客户区域的实心绿色矩形。我想让这个矩形在窗口改变大小时重新绘制并填满整个窗口。最初我所做的是从WM_SIZE处理程序发布WM_PAINT消息。
它能够工作,但如果我快速移动鼠标,我会看到绿色矩形周围有一些未绘制(白色)区域(实际上只有一两个边靠近鼠标的位置)。我对问题的理解是,处理用户输入(鼠标)的系统线程比我处理WM_PAINT消息的线程更快。这意味着在我开始绘制更新的矩形(其大小取自WM_SIZE)时,鼠标实际上已经移动了一点,系统会绘制一个与我试图用绿色填充的不同的新窗口框架。这会在调整大小期间创建未填充的边框旁边的区域。
当我停止调整大小时,绿色最终填满整个窗口,但在调整大小期间,边框附近会出现闪烁,这很烦人。为了解决这个问题,我尝试了以下方法。
这也行不通。就我所理解的,解决问题的方法在于以某种方式减缓鼠标的速度,使得它仅在绘画完成后(拖动窗口的角或边缘)才移动到屏幕的下一个位置。
有什么想法可以实现这一点吗?或者我对问题和解决方案的看法存在根本性错误?
// ====================================================
更新
我做了几个实验,以下是我的发现:
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可能会有所帮助,但这似乎超出了我们的控制范围(除非有一种简单的方法可以做到这一点,我只是不知道)。
为了简单起见,我们假设它是一个填满整个客户区域的实心绿色矩形。我想让这个矩形在窗口改变大小时重新绘制并填满整个窗口。最初我所做的是从WM_SIZE处理程序发布WM_PAINT消息。
它能够工作,但如果我快速移动鼠标,我会看到绿色矩形周围有一些未绘制(白色)区域(实际上只有一两个边靠近鼠标的位置)。我对问题的理解是,处理用户输入(鼠标)的系统线程比我处理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;
这也行不通。就我所理解的,解决问题的方法在于以某种方式减缓鼠标的速度,使得它仅在绘画完成后(拖动窗口的角或边缘)才移动到屏幕的下一个位置。
有什么想法可以实现这一点吗?或者我对问题和解决方案的看法存在根本性错误?
// ====================================================
更新
我做了几个实验,以下是我的发现:
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可能会有所帮助,但这似乎超出了我们的控制范围(除非有一种简单的方法可以做到这一点,我只是不知道)。