调整大小时在窗口中绘制未涂漆的边框

我所遇到的问题似乎微不足道,但我找不到解决问题的方法。 这里是。 我有一个窗口,里面有一些图形。

为了简单起见,我们可以说它是一个坚实的绿色矩形,可以填充整个窗口的客户区域。 我想要重绘这个矩形并在每次窗口改变大小时填充整个窗口。 我最初做的是这个。 我从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可能会有所帮助,但是这似乎超出了我们的控制范围(除非有一种简单的方法可以做到我不知道的)。


您不应该自己发布或发送WM_PAINT消息。 相反,使用:: InvalidateRect使窗口的部分无效,并让Windows决定何时发送WM_PAINT消息。


Windows有意这样工作。 通常认为响应用户(即鼠标)比拥有完全最新的绘制窗口更重要。

如果您总是在WM_PAINT处理程序中绘制整个窗口,则可以通过重写WM_ERASEBKGND处理程序来消除大量闪烁,并且无需执行任何操作即可返回。

如果你真的坚持比鼠标响应更喜欢窗口更新,请使用RDW_UPDATENOW标志将InvalidateRect调用替换为RedrawWindow调用。

编辑:你的观察是有道理的。 WM_SIZING出现在窗口大小调整之前,让您有机会修改大小和/或位置。 即使在绘制边框之前,您也可以尝试在WM_NCPAINT处理程序中绘制客户区。 绘制完成后,您可以验证客户区以防止再次绘制。


手动发布WM_PAINTWM_SIZE是个不错的主意。 你可以做的一件奇怪的疯狂的事情是,将新调整大小的坐标记录在WM_SIZE中的RECT中,使用MoveWindow将窗口大小改回到前一个,然后使用WM_PAINT中的 MoveWindow再次手动调整大小完成绘画后的消息。

另一种可能的解决方案是不断地用窗口填充颜色,而不管屏幕是否调整大小。 即

// in the WinMain function
if (GetMessage(&msg,NULL,NULL,0))
{
    TranslateMessage(&msg,NULL,NULL);
    DispatchMessage(&msg,NULL,NULL);
}
else
{
     // fill your window with the color in here
}

或者,当然,您可以将窗口的背景颜色设置为绿色,而不是自己完成所有的绘画:

// Before RegisterClass or RegisterClassEx
// wincl is a WNDCLASS or WNDCLASSEX
wincl.hbrBackground = CreateSolidBrush(RGB(50, 238, 50));
链接地址: http://www.djcxy.com/p/50297.html

上一篇: Drawing in window while resizing leaves Unpainted border

下一篇: Converting Color Bitmap to Grayscale in C++