调整大小时绘制窗口未绘制边框 [英] Drawing in window while resizing leaves Unpainted border

查看:125
本文介绍了调整大小时绘制窗口未绘制边框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



为了简单起见,它是一个实心的绿色矩形,填充窗口的整个客户区。我想要重绘这个矩形,并在每次窗口改变其大小时填充整个窗口。我最初是这样做的。我从 WM_SIZE 处理程序发布了 WM_PAINT 消息。



它可以工作,但是如果我快速移动鼠标,我看到绿色矩形周围有一点未上漆(白色)区域(实际上只有一侧或两侧,靠近鼠标是)。我对这个问题的理解是,处理用户输入(鼠标)的系统线程比我的WM_PAINT消息处理程序工作得更快。这意味着,当我开始绘制一个更新的矩形(它的大小是从WM_SIZE),鼠标实际上移动一点,系统绘制一个新的窗口框架,这是不同的,我想用绿色填充。



当我停止调整大小时,绿色最终填满整个窗口,但在调整大小时有一些闪烁的发生close到边界是烦人的。为了解决这个问题,我尝试了以下。

  bool finishedPainting; 
RECT windowRect;

case WM_PAINT:
// .....这里画的

finishedPainting = TRUE;
break;

case WM_SIZE:
// ...一些动作

// posting WM_PAINT
InvalidateRect(hWnd,NULL,FALSE);
PostMessage(hWnd,WM_PAINT,0,0);
break;

case WM_SIZING:
//这应该可以防止系统通过
//新窗口大小为WM_SIZE
if(!finishedPainting)memcpy((void * )lParam,& windowRect,sizeof(windowRect));
else {
//记住当前窗口大小供以后使用
memcpy(& windowRect,(void *)lParam,sizeof(windowRect));
finishedPainting = FALSE;
}
return TRUE;

它不工作。作为一个微小的变化,我也尝试了这个。

  bool finishedPainting; 
POINT cursorPos;

case WM_PAINT:
// .....这里画的

finishedPainting = TRUE;
break;

case WM_SIZE:
if(!finishedPainting)SetCursorPos(cursorPos.x,cursorPos.y);
else {
finishedPainting = FALSE;
GetCursorPos(& cursorPos);

// ...一些动作

InvalidateRect(hWnd,NULL,FALSE);
PostMessage(hWnd,WM_PAINT,0,0);
}
break;

这也不行。据我所知,解决问题的方法是以某种方式放慢鼠标的速度,使它只在绘画完成后移动到屏幕上的下一个位置(拖动角落或窗口的边上)。

任何想法如何实现呢?或者也许有一些根本上错误的方式,我看到的问题,解决方案在别的地方。



// ============= ===================================



更新



我做了一些实验,这是我发现的



,消息的顺序是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消息。


The problem that I have seems to be trivial, but I cannot find a way to solve it. Here it is. I have a window with some graphics in it.

For simplicity lets say it's a solid green rectangle which fills the entire client area of the window. I want this rectangle to be redrawn and to fill the entire window every time the window changes its size. What I did originally was this. I posted WM_PAINT message from WM_SIZE handler.

It works, but if I move mouse fast I see a bit of unpainted (white) area around the green rectangle (actually one or two sides only, close to where mouse is). My understanding of the problem is that system thread which handles user input (mouse) works faster than my handler of WM_PAINT message. It means that by the time I start drawing an updated rectangle (its size is taken from WM_SIZE), mouse actually moves a little bit and system draws a new window frame which is different from what I'm trying to fill with the green. This creates unfilled areas next to borders which move during resizing.

When I stop resizing, green eventually fills the entire window, but during resizing there is a bit of flickering happening close to borders which is annoying. In order to solve the problem I tried the following.

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;

It doesnt' work. As a slight variation, I also tried this.

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;

This also doesn't work. As far as I understand the solution to the problem lies in somehow slowing the mouse down so that it moves to the next position on the screen (dragging the corner or the side of the window with it) only after the painting is finished.

Any ideas how to achieve this? Or maybe there is something fundamentally wrong with the way I see the problem and solution lies somewhere else?

// ====================================================

Update

I did a few experiments and here is what I found

1) When resizing, the sequence of messages is WM_SIZING - WM_NCPAINT - WM_SIZE - WM_PAINT. This looks a bit strange to me. I would expect WM_SIZE to follow WM_SIZING without interruption by WM_NCPAINT

2) In each message handler I was checking the width of a window during resizing (for simplicity I was only changing width). Surprisingly, the width measured in WM_SIZE turned out to be different from the one in WM_SIZING, but the same as in WM_NCPAINT and WM_PAINT. This is not a problem as such, just a wierd fact.

3) I came to the conclusion that there are two major causes for flicker happening near the window borders. The first one is that WM_NCPAINT comes before WM_PAINT. Imagine that you are stretching your window. The new frame will appear first (WM_NCPAINT comes first), then WM_PAINT fills the client area. A human eye catches that short period of time when the new frame is already on the screen, but it is empty. Even if you specify that you don't want window background to be deleted before repainting, still newly added area is empty and you can see it for a split second. This reason for flicker is best demonstrated when you grab the right window edge and move it quickly to the right. The other reason for flickering effect is less obvious and best seen when you grab the left window edge and move it to the left. During this move you will see unfilled areas along the RIGHT edge. As far as I understand the effect is caused by this. When user is doing resize Windows does the following: A) it sends WM_NCPAINT to draw the new frame, B) it copies the content of the old client area into the new top left window corner (in our case it moved to the left), C) it sends WM_PAINT to fill the new client area. However during stage B for some reason Windows produces those unfilled areas along the right edge, although it seems like it shouldn't because the old content should just stay where it is until it gets repainted over during WM_PAINT.

Ok, the question remains - how to get rid of those artefacts during resizing. As far as I can see now it is impossible to do using standard techniques and functions, because they are caused by the sequence of steps Windows performs during resizing. Swapping WM_NCPAINT and WM_PAINT would probably help, but this seems to be beyond our control (unless there is a simple way to do that which I just don't know about).

解决方案

You shouldn't post or send WM_PAINT messages yourself. Rather, use ::InvalidateRect to invalidate parts of your window and let Windows decide when to send the WM_PAINT messages.

这篇关于调整大小时绘制窗口未绘制边框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆