使用Win32 API和GDI绘制窗口的无效区域 [英] Paint invalid areas of a window using Win32 API and GDI

查看:401
本文介绍了使用Win32 API和GDI绘制窗口的无效区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

First, I'm new here, so hello world!

我正在研究一个轻量级的控件库.每个控件都是名为"GraphicElement"的类的实例,并且没有句柄.我创建了一个事件调度程序,它可以按预期工作,但是我很难控制控件的绘制.它们存储在树中,当我穿过这棵树时,我会对其进行绘画.我还使用了后缓冲区来确保窗口的内容不会闪烁.

I'm working on a little lightweight controls library. Each control is an instance of a class named "GraphicElement", and doesn't have a handle. I created an event dispatcher and it works as expected but I struggle with the painting of my controls. They are stored in a tree, and I paint them as I go through this tree. I also use a back buffer to ensure the window's content doesn't flicker.

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

Everything works fine, but when I move one of the controls, this happens:

.

当然,我可以使整个窗口无效并重新粉刷,这从理论上解决了我的问题,但是我想避免这样做,尤其是在不必要且出于性能原因的情况下.

Of course, I can invalidate and repaint the whole window, which theoretically solves my problem, but I'd like to avoid doing so, especially when it's not necessary and for performance reasons.

这是一个例子:

我想移动R2,然后重新绘制空白点(我的意思是R2的旧位置),而无需重新绘制R4和R5(可能还有许多其他图形).

I'd like to move R2, then repaint the empty spot (I mean the old location of R2) without redrawing R4 and R5 (and maybe many others).

如何重画消失"的背景部分?我是否需要重新绘制整个背景以及所有控件? 我不会在这里发布所有代码,因为它很长,并且还可以处理其他事件,例如事件,但是正如我之前所说,我在遍历一棵树时绘制控件,因此其中没有什么疯狂的地方.

How to repaint the part of the background which "disappeared" ? Will I have to repaint the whole background, and so all my controls ? I won't post all my code here because it's pretty long and it also handles other things like events, but as I said before, I draw my controls as I iterate through a tree, so there's nothing crazy in it.

在此先感谢您的帮助,如果我不清楚,请多谢.

Thank you in advance for your help, and sorry if I'm being not clear.

这是一些代码,但是正如我之前说过的,如果我使窗口的工作区无效,那么它就像一个超级按钮,但我想避免这样做.

Here's some code, but as I said before if I invalidate the client area of the window it works like a charm, but I want to avoid doing that.

Windows发送WM_PAINT消息时,将调用此方法("render"):

This method ("render") is called when Windows sends a WM_PAINT message :

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":

And here's the method "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

最后,当控件被调整大小或移动时,使用此功能会使区域无效:

Finally, when a control gets resized or moved, its area is invalidated using this function :

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

推荐答案

我不知道您所说的没有句柄的轻量级控件"是什么意思,但是我想它们是简单的C ++类(不是真的) 控件",必须在父窗口的工作区中绘制.

I don't know what you mean by "lightweight control that doesn't have a handle", but I guess they are simple C++ classes (and not true "controls) that must be drawn on the parent window's client area.

问题"是WM_PAINT消息是低优先级消息,如果窗口在其客户区中具有无效部分,则在应用程序屈服之前发送该消息.

The "problem" is that the WM_PAINT message is a low-priority one, sent if the window has an invalid part in its client area, just before the application yields.

您首先应该阅读的文档是: 绘画和绘图

The documentation you should read first is: Painting and Drawing

我建议的实现方式,因为我已经使用了很多次,并且效果很好,所以是两种方法的组合:

The implementation I would suggest, as I have used it quite a few times and works really well, is a combination of both methods:

  • 使用PAINTSTRUCT结构的rcPaint成员,处理WM_PAINT消息(和BeginPaint()/EndPaint()函数)以绘制整个客户端窗口(或其一部分),如果需要更多的话需要优化"的实现).请注意,WM_PAINT消息的发送可能是由于移动,调整大小,将窗口置于前景或显示窗口的一部分先前被另一个窗口遮挡的缘故,这是由于用户的行为造成的,此外还有编程方式上的无效全部或部分.因此,对于此消息,您应该在其当前位置绘制父窗口和所有控件.
  • 使用GetDC()/ReleaseDC()函数仅绘制受诸如添加,删除或移动控件之类的动作影响的窗口部分.这种绘制会立即发生,而无需等待发送WM_PAINT消息.您应该填充控件先前占用的区域,然后将控件绘制到新位置.您不应使客户区的任何部分无效,因为这会导致发送另一条WM_PAINT消息.
  • 控制绘图函数应该采用HDC参数(在其他所有需要的参数中),以便可以由两种绘图方法(由GetDC()函数返回的句柄)使用. /li>
  • Process the WM_PAINT message (and the BeginPaint()/EndPaint() functions) to paint the whole client window (or a part of it, using the rcPaint member of the PAINTSTRUCT structure, if a more "optimized" implementation is desired). Please note that the WM_PAINT message may be sent as a result of moving, resizing, bringing the window in foreground, or revealing a part of the window previously obscured by another one, that is due to user actions, in addition to programmatically invalidating the whole or part of it. So in response to this message you should draw the parent window and all the controls in their current position.
  • Use the GetDC()/ReleaseDC() functions to draw only the part of the window affected by actions like adding, deleting, or moving a control. This kind of drawing takes place immediately, not waiting for the WM_PAINT message to be sent. You should fill the area previously occupied by the control and draw the control in its new position. You should not invalidate any part of the client area, as this would cause another WM_PAINT message to be sent.
  • The control drawing functions should be taking a HDC parameter (among any others needed), so as to be usable by both drawing methods (the handles returned by either the BeginPaint() or the GetDC() functions).

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

I have used this technique to make image-processing applications (eg have the user selecting a part of the image and drawing/restoring the rectangle selected) and unattended monitor applications.

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

An alternative, simpler implementation (employing only "Painting" but not "Drawing") could be:

  • 调整控件大小或移动控件时,仅会使控件占用的新旧区域无效.
  • WM_PAINT消息的处理通常如上所述,但是应该对其进行修改,以便仅填充PAINTSTRUCT结构的rcPaint成员中的矩形,并仅绘制与上述矩形相交的控件.
  • When a control is resized or moved invalidate only the old and new areas occupied by the control.
  • Processing of the WM_PAINT message generally as above, but it should be modified so as to fill only the rectangle in the rcPaint member of the PAINTSTRUCT structure, and draw only the controls intersecting with the above rectangle.

这篇关于使用Win32 API和GDI绘制窗口的无效区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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