使用Win32 API和GDI绘制窗口的无效区域

时间:2017-05-06 21:18:22

标签: c++ winapi gdi

首先,我是新来的,所以你好世界!

我正在开发一个轻量级的控件库。每个控件都是名为“GraphicElement”的类的实例,并且没有句柄。我创建了一个事件调度程序,它按预期工作,但我很难对我的控件绘画。它们存储在树中,当我浏览这棵树时,我会将它们绘制出来。我还使用后台缓冲区来确保窗口的内容不会闪烁。

一切正常,但当我移动其中一个控件时,会发生这种情况:

当然,我可以对整个窗口进行无效和重绘,这在理论上解决了我的问题,但我想避免这样做,特别是当它没有必要时以及出于性能原因时。

以下是一个例子:

我想移动R2,然后重新绘制空白点(我的意思是R2的旧位置),而不重绘R4和R5(可能还有很多其他的)。

如何重新绘制“消失”的背景部分?我是否需要重新整理整个背景,以及我的所有控件? 我不会在这里发布我的所有代码,因为它很长,而且它还处理其他事情,比如事件,但正如我之前说过的,我在迭代树时绘制控件,所以没有什么可以疯狂的。

提前感谢您的帮助,如果我不清楚,请抱歉。

编辑:这是一些代码,但正如我之前所说,如果我使窗口的客户区无效,它就像一个魅力,但我想避免这样做。

当Windows发送WM_PAINT消息时调用此方法(“render”):

m_hdcMem = CreateCompatibleDC(hdc);
m_bmpMem = CreateCompatibleBitmap(hdc, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top);
m_bmpOld = (HBITMAP)SelectObject(m_hdcMem, m_bmpMem);
m_background->predraw(m_hdcMem); // draws the client area, which is an instance of GraphicElement
BitBlt(hdc, m_rect.left, m_rect.top, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top, m_hdcMem, 0, 0, SRCCOPY);
SelectObject(m_hdcMem, m_bmpOld);
DeleteObject(m_bmpMem);
DeleteDC(m_hdcMem);

这是方法“predraw”:

draw(hdc); // draws the current control

for (std::vector<GraphicElement*>::iterator it = m_children.begin(); it != m_children.end(); ++it)
        (*it)->predraw(hdc); // "predraws" the other controls

最后,当控件调整大小或移动时,使用此函数使其区域无效:

InvalidateRect(m_parentHwnd, lpRect, FALSE); // If I invalidate the whole window, my code works perfectly, but I'd like to know how to paint parts of my window

1 个答案:

答案 0 :(得分:1)

我不知道“没有句柄的轻量级控件”是什么意思,但我猜它们是简单的C ++类(而不是真正的“控件”)必须在父窗口的客户区域上绘制。

“问题”是WM_PAINT消息是低优先级消息,如果窗口在其客户区域中具有无效部分,就在应用程序产生之前发送。

您应首先阅读的文档是: Painting and Drawing

我建议的实现,因为我已经使用了很多次并且工作得很好,是两种方法的组合:

  • 处理WM_PAINT消息(以及BeginPaint() / EndPaint()函数)以使用rcPaint成员绘制整个客户端窗口(或其中的一部分) PAINTSTRUCT结构,如果需要更“优化”的实现)。请注意,WM_PAINT消息可能会因为移动,调整大小,将窗口置于前台,或者显示先前被另一个窗口遮挡的窗口的一部分而发送,这是由于用户操作,除了以编程方式使其全部或部分无效。因此,为了响应此消息,您应该绘制父窗口和当前位置的所有控件。
  • 使用GetDC() / ReleaseDC()函数仅绘制受添加,删除或移动控件等操作影响的窗口部分。这种绘图立即发生,而不是等待发送WM_PAINT消息。您应填充控件先前占用的区域,并将控件绘制到新位置。您不应该使客户区的任何部分无效,因为这会导致发送另一条WM_PAINT消息。
  • 控制绘图函数应该采用HDC参数(在任何其他需要的参数中),以便两种绘图方法(BeginPaint()或{{1]返回的句柄可用功能)。

我已经使用这种技术制作图像处理应用程序(例如让用户选择图像的一部分并绘制/恢复选定的矩形)和无人值守的监视器应用程序。

另一种更简单的实现(仅使用“绘画”但不使用“绘图”)可以是:

  • 当控件调整大小或移动时,仅使控件占用的旧区域和新区域无效。
  • 通常如上所述处理GetDC()消息,但应对其进行修改,以便仅填充WM_PAINT结构的rcPaint成员中的矩形,并仅绘制控件与上面的矩形相交。