c++ 在窗口中绘制,同时调整叶的大小未绘制边框

4nkexdtk  于 2023-06-07  发布在  其他
关注(0)|答案(4)|浏览(152)

我遇到的问题看似微不足道,但我找不到解决它的方法。在这里。我有一个窗口,里面有一些图形。
为了简单起见,我们假设它是一个实心的绿色矩形,填充了窗口的整个工作区。我希望这个矩形被重新绘制,并填补了整个窗口的窗口每次改变其大小。我最初做的是这个。我发布了来自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可能会有所帮助,但这似乎超出了我们的控制范围(除非有一种简单的方法来做到这一点,我只是不知道)。

6gpjuf90

6gpjuf901#

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

weylhg0b

weylhg0b2#

Windows是故意这样工作的。它通常被认为是更重要的是响应用户(即鼠标)而不是拥有一个完全最新的绘制窗口。
如果您总是在WM_PAINT处理程序中绘制整个窗口,则可以通过重写WM_ERASEBKGND处理程序并返回而不做任何操作来消除大量 Flink 。
如果你真的坚持窗口更新优于鼠标响应,用一个使用RDW_UPDATENOW标志的RedrawWindow调用替换InvalidateRect调用。
答:你的观察是有意义的。WM_SIZING出现在窗口调整大小之前,让你有机会修改大小和/或位置。您可以尝试在WM_NCPAINT处理程序中绘制工作区,甚至在绘制边框之前。绘制完后,可以验证工作区以防止再次绘制。

ffscu2ro

ffscu2ro3#

手动发布WM_PAINTWM_SIZE是个坏主意。一个奇怪的黑客疯狂的事情,你可以做的,是记录在一个RECTWM_SIZE,使用MoveWindow改变窗口的大小 * 回 * 它的前一个,然后手动调整它 * 再次 * 使用MoveWindowWM_PAINT消息 * 后 * 你做的画。
另一个可能的解决方案是不断地用颜色填充你的窗口,不管屏幕是否调整了大小。即

// 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));
iq3niunx

iq3niunx4#

我也有同样的问题。有很多词来描述我的解决方案,所以总结一下,我只是改变了窗口类的样式,使其具有CS_VREDRAW| CS_HREDRAW:

void WindowsGUI::initialise(){
    gWindowClass.cbSize = sizeof(gWindowClass);
    gWindowClass.lpfnWndProc = windowProcess;
    gWindowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    gWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    gWindowClass.hbrBackground = (HBRUSH) GetStockObject (DKGRAY_BRUSH + 1);
    gWindowClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    gWindowClass.style = **CS_VREDRAW** | **CS_HREDRAW**;
    glGUI = this;
}

相关问题