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

C++ 使用Win32 API和GDI绘制窗口的无效区域,c++,winapi,gdi,C++,Winapi,Gdi,首先,我是新来的,世界你好 我正在开发一个轻量级控件库。每个控件都是名为“GraphicElement”的类的实例,没有句柄。我创建了一个事件调度器,它按预期工作,但我很难画出我的控件。它们存放在一棵树上,我在穿过这棵树的时候画它们。我还使用了一个后缓冲区来确保窗口的内容不会闪烁 一切正常,但当我移动其中一个控件时,会发生以下情况: 当然,我可以使整个窗口失效并重新绘制,这在理论上解决了我的问题,但我希望避免这样做,尤其是在没有必要的情况下,以及出于性能原因 下面是一个例子: 我想移动R2,

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

我正在开发一个轻量级控件库。每个控件都是名为“GraphicElement”的类的实例,没有句柄。我创建了一个事件调度器,它按预期工作,但我很难画出我的控件。它们存放在一棵树上,我在穿过这棵树的时候画它们。我还使用了一个后缓冲区来确保窗口的内容不会闪烁

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

当然,我可以使整个窗口失效并重新绘制,这在理论上解决了我的问题,但我希望避免这样做,尤其是在没有必要的情况下,以及出于性能原因

下面是一个例子:

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

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

提前感谢您的帮助,如果我不清楚,请原谅

编辑:这里有一些代码,但正如我之前所说的,如果我使窗口的客户端区域无效,它会像一个符咒一样工作,但我想避免这样做

当Windows发送WM_PAINT消息时,将调用此方法(“渲染”):

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);
这里是“预绘制”方法:


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

WM_PAINT
消息的优先级较低,如果窗口的客户端区域中有无效部分,则在应用程序生成之前发送该消息

您应该首先阅读的文档是:

我建议的实现是两种方法的结合,因为我已经多次使用它,而且效果非常好:

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


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

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

我不知道你所说的“轻量级控件没有句柄”是什么意思,但我猜它们是简单的C++类(而不是真的)控件,必须在父窗口的客户端区域绘制。 “问题”在于
WM_PAINT
消息的优先级较低,如果窗口的客户端区域中有无效部分,则在应用程序生成之前发送该消息

您应该首先阅读的文档是:

我建议的实现是两种方法的结合,因为我已经多次使用它,而且效果非常好:

  • 处理
    WM_PAINT
    消息(以及
    BeginPaint()
    /
    EndPaint()
    函数)以绘制整个客户端窗口(或其中的一部分,如果需要更“优化”的实现,则使用
    PAINTSTRUCT
    结构的
    rcPaint
    成员)。请注意,
    WM_PAINT
    消息的发送可能是由于移动、调整大小、将窗口置于前台或显示先前被另一个窗口遮挡的窗口部分,这是由于用户操作,以及以编程方式使整个或部分窗口无效。因此,为了响应此消息,您应该在当前位置绘制父窗口和所有控件
  • 使用
    GetDC()
    /
    ReleaseDC()
    函数仅绘制受添加、删除或移动控件等操作影响的窗口部分。这种绘图是立即进行的,而不是等待<
    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