使用 Win32 API 和 GDI 绘制 window 的无效区域
Paint invalid areas of a window using Win32 API and GDI
首先,我是新来的,世界您好!
我正在开发一个小型轻量级控件库。每个控件都是名为 "GraphicElement" 的 class 的实例,并且没有句柄。我创建了一个事件调度程序,它按预期工作,但我在绘制控件时遇到了困难。它们被储存在一棵树上,我在穿过这棵树时画它们。我还使用后台缓冲区来确保 window 的内容不会闪烁。
一切正常,但是当我移动其中一个控件时,会发生这种情况:
.
当然,我可以使整个 window 无效并重新绘制,这在理论上解决了我的问题,但我想避免这样做,尤其是在不必要和出于性能原因时。
这是一个例子:
我想移动 R2,然后重新绘制空白点(我指的是 R2 的旧位置)而不重新绘制 R4 和 R5(可能还有许多其他位置)。
如何重绘"disappeared"的背景部分?我是否必须重新绘制整个背景以及我所有的控件?
我不会在这里 post 我所有的代码,因为它很长,而且它还处理事件等其他事情,但正如我之前所说,我在遍历树时绘制控件,因此其中没有什么疯狂的地方。
预先感谢您的帮助,如果我不清楚,请见谅。
编辑:这是一些代码,但正如我之前所说,如果我使 window 的客户区无效,它就像一个魅力,但我想避免这样做。
当 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
我不知道你所说的 "lightweight control that doesn't have a handle" 是什么意思,但我猜它们是简单的 C++ 类(而不是真正的“控件”),必须在父级上绘制 window的客户区。
"problem" 是 WM_PAINT
消息是低优先级消息,如果 window 在其客户区有无效部分,就在应用程序让步之前发送。
您应该首先阅读的文档是:Painting and Drawing
我建议的实现是两种方法的组合:
- 处理
WM_PAINT
消息(和 BeginPaint()
/EndPaint()
函数)以绘制整个客户端 window(或其中的一部分,使用 [= PAINTSTRUCT
结构的 14=] 成员,如果需要更多 "optimized" 实现)。请注意,WM_PAINT
消息的发送可能是由于移动、调整大小、将 window 置于前景或显示之前被另一部分遮挡的 window 的一部分,即由于用户操作,除了以编程方式使其全部或部分无效之外。因此,为了响应此消息,您应该在当前位置绘制父级 window 和所有控件。
- 使用
GetDC()
/ReleaseDC()
函数仅绘制受添加、删除或移动控件等操作影响的 window 部分。这种绘图立即发生,不等待 WM_PAINT
消息发送。您应该填充控件先前占据的区域并在其新位置绘制控件。您不应使客户区的任何部分无效,因为这会导致发送另一条 WM_PAINT
消息。
- 控制绘图函数应该采用
HDC
参数(在任何其他需要的参数中),以便两种绘图方法都可以使用(BeginPaint()
或 GetDC()
函数)。
我已经使用这种技术制作图像处理应用程序(例如,让用户选择图像的一部分并 drawing/restoring 选择矩形)和无人值守的监视器应用程序。
另一种更简单的实现方式(仅使用 "Painting" 而不是 "Drawing")可以是:
- 调整控件大小或移动控件时,仅使控件占用的旧区域和新区域无效。
WM_PAINT
消息的处理大体同上,但要修改为只填充PAINTSTRUCT
结构体rcPaint
成员中的矩形,只绘制与上述矩形相交的控件。
首先,我是新来的,世界您好!
我正在开发一个小型轻量级控件库。每个控件都是名为 "GraphicElement" 的 class 的实例,并且没有句柄。我创建了一个事件调度程序,它按预期工作,但我在绘制控件时遇到了困难。它们被储存在一棵树上,我在穿过这棵树时画它们。我还使用后台缓冲区来确保 window 的内容不会闪烁。
一切正常,但是当我移动其中一个控件时,会发生这种情况:
当然,我可以使整个 window 无效并重新绘制,这在理论上解决了我的问题,但我想避免这样做,尤其是在不必要和出于性能原因时。
这是一个例子:
我想移动 R2,然后重新绘制空白点(我指的是 R2 的旧位置)而不重新绘制 R4 和 R5(可能还有许多其他位置)。
如何重绘"disappeared"的背景部分?我是否必须重新绘制整个背景以及我所有的控件? 我不会在这里 post 我所有的代码,因为它很长,而且它还处理事件等其他事情,但正如我之前所说,我在遍历树时绘制控件,因此其中没有什么疯狂的地方。
预先感谢您的帮助,如果我不清楚,请见谅。
编辑:这是一些代码,但正如我之前所说,如果我使 window 的客户区无效,它就像一个魅力,但我想避免这样做。
当 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
我不知道你所说的 "lightweight control that doesn't have a handle" 是什么意思,但我猜它们是简单的 C++ 类(而不是真正的“控件”),必须在父级上绘制 window的客户区。
"problem" 是 WM_PAINT
消息是低优先级消息,如果 window 在其客户区有无效部分,就在应用程序让步之前发送。
您应该首先阅读的文档是:Painting and Drawing
我建议的实现是两种方法的组合:
- 处理
WM_PAINT
消息(和BeginPaint()
/EndPaint()
函数)以绘制整个客户端 window(或其中的一部分,使用 [=PAINTSTRUCT
结构的 14=] 成员,如果需要更多 "optimized" 实现)。请注意,WM_PAINT
消息的发送可能是由于移动、调整大小、将 window 置于前景或显示之前被另一部分遮挡的 window 的一部分,即由于用户操作,除了以编程方式使其全部或部分无效之外。因此,为了响应此消息,您应该在当前位置绘制父级 window 和所有控件。 - 使用
GetDC()
/ReleaseDC()
函数仅绘制受添加、删除或移动控件等操作影响的 window 部分。这种绘图立即发生,不等待WM_PAINT
消息发送。您应该填充控件先前占据的区域并在其新位置绘制控件。您不应使客户区的任何部分无效,因为这会导致发送另一条WM_PAINT
消息。 - 控制绘图函数应该采用
HDC
参数(在任何其他需要的参数中),以便两种绘图方法都可以使用(BeginPaint()
或GetDC()
函数)。
我已经使用这种技术制作图像处理应用程序(例如,让用户选择图像的一部分并 drawing/restoring 选择矩形)和无人值守的监视器应用程序。
另一种更简单的实现方式(仅使用 "Painting" 而不是 "Drawing")可以是:
- 调整控件大小或移动控件时,仅使控件占用的旧区域和新区域无效。
WM_PAINT
消息的处理大体同上,但要修改为只填充PAINTSTRUCT
结构体rcPaint
成员中的矩形,只绘制与上述矩形相交的控件。