Paint invalid areas of a window using Win32 API and GDI

强颜欢笑 提交于 2020-01-25 21:14:52

问题


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

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.

Here's an example:

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.

EDIT: 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.

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);

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

回答1:


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.

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:

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

  • 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.


来源:https://stackoverflow.com/questions/43825479/paint-invalid-areas-of-a-window-using-win32-api-and-gdi

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!