How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?

别说谁变了你拦得住时间么 提交于 2019-11-28 16:42:10

You can't prevent painting during resizing, but you can (with care) prevent repainting which is where flicker comes from. first, the bitblt.

There a two ways to stop the bitblt thing.

If you own the class of the top level window, then just register it with the CS_HREDRAW | CS_VREDRAW styles. This will cause a resize of your window to invalidate the entire client area, rather than trying to guess which bits are not going to change and bitblting.

If you don't own the class, but do have the ability to control message handling (true for most dialog boxes). The default processing of WM_NCCALCSIZE is where the class styles CS_HREDRAW and CS_VREDRAW are handled, The default behavior is to return WVR_HREDRAW | WVR_VREDRAW from processing WM_NCCALCSIZE when the class has CS_HREDRAW | CS_VREDRAW.

So if you can intercept WM_NCCALCSIZE, you can force the return of these values after calling DefWindowProc to do the other normal processing.

You can listen to WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE to know when resizing of your window starts and stops, and use that to temporarily disable or modify the way your drawing and/or layout code works to minimize the flashing. What exactly you want to do to modify this code will depend on what your normal code normally does in WM_SIZE WM_PAINT and WM_ERASEBKGND.

When you paint the background of your dialog box, you need to not paint behind any of the child windows. making sure that the dialog has WS_CLIPCHILDREN solves this, so you have this handled already.

When you do move the child windows, Make sure that you use BeginDeferWindowPos / EndDefwindowPos so that all of the repainting happens at once. Otherwise you will get a bunch of flashing as each window redraws their nonclient area on each SetWindowPos call.

If I understood the question properly, it's exactly the question Raymond addressed today.

For some controls, you can use WM_PRINT message to make the control draw into a DC. But that doesn't really solve your primary problem, which is that you want Windows to NOT draw anything during resize, but to let you do it all.

And the answer is that you just can't do what you want as long as you have child windows.

The way I ended up solving this eventually in my own code is to switch to using Windowless Controls. Since they have no window of their own, they always draw at the same time (and into the same DC) as their parent window. This allows me to use simple double buffering to completely remove flicker. I can even trivially suppress painting of the children when I need to just by not calling their draw routine inside the parent's draw routine.

This is the only way I know of to completely get rid of flicker and tearing during resize operations.

Here's a 2018 update, since I just ran through the very same gauntlet as you.

The "final solution" in your question, and the related answers, that mention tricks with WM_NCCALCSIZE and CS_HREDRAW|CS_VREDRAW are good for preventing Windows XP/Vista/7 from doing the BitBlt that molests your client area during resizing. It might even be useful to mention a similar trick: you can intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.

And some people mentioned that your WM_NCCALCSIZE trick no longer works in Windows 10. I think that might be because the code you wrote returns WVR_ALIGNLEFT|WVR_ALIGNTOP when it should be returning WVR_VALIDRECTS in order for the two rectangles you constructed (nccs_params->rgrc[1] and nccs_params->rgrc[2]) to be used by Windows, at least according to the very skimpy dox in the MSDN pages for WM_NCCALCSIZE and NCCALCSIZE_PARAMS. It's possible that Windows 10 is more strict about that return value; I would try it out.

However, even if we assume that we can convince Windows 10 not to do BitBlt inside SetWindowPos(), it turns out there's a new problem...

Windows 10 (and possibly also Windows 8) adds another layer of client area molestation on top of the old legacy molestation from XP/Vista/7.

Under Windows 10, apps do not draw directly to the framebuffer, but instead draw into offscreen buffers that the Aero Window manager (DWM.exe) composites.

It turns out that DWM will sometimes decide to "help" you by drawing its own content over your client area (sort of like a BitBlt but even more perverse and even further out of your control).

So in order to be free of client area molestation, we still need to get WM_NCCALCSIZE under control but we also need to prevent DWM from messing with your pixels.

I was fighting with exactly the same problem and created a roundup Question/Answer which brings together 10 years of posts on this topic and offers some new insights (too long to paste the content here in this question). The BitBlt mentioned above is no longer the only problem, as of Windows Vista. Enjoy:

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

If you can find a place to plug it in, CWnd::LockWindowUpdates() will prevent any drawing from occuring until after you unlock the updates.

But keep in mind this is a hack, and a fairly ugly one at that. Your window will look terrible during resizes. If the problem you are having is flickering during resizes, then the best thing to do is diagnose the flickering, rather than hiding the flickering by blocking paints.

One thing to look for are redraw commands that get called too often during the resize. If you r window's controls are calling RedrawWindow() with the RDW_UPDATENOW flag specified, it is going to repaint then and there. But you can strip out that flag and specify RDW_INVALIDATE instead, which tells the control to invalidate the window without repainting. It will repaint at idle time, keeping the display fresh without spazzing out.

peterchen

There are various approaches, but I found the only one that can be used generally is double buffering: draw to an offscreen buffer, then blit the entire buffer to screen.

That comes for free in Vista Aero and above, so your pain might be shortlived.

I am not aware of a general double-buffering implementation for windows and system controls under XP, However, here are some things to explore:

Keith Rule's CMemDC for double-buffering anything you draw yourself with GDI
WS_EX_COMPOSITED Window style (see the remarks section, and something here on stackoverflow)

there is only one way to effectively diagnose repainting problems - remote debugging.

Get a 2nd PC. Install MSVSMON on it. Add a post build step or utility project that copies your build products to the remote PC.

Now you should be able to place breakpoints in WM_PAINT handlers, WM_SIZE handlers and so on and actually trace through your dialog code as it performs the size and redraw. If you download symbols from the MS symbol servers you will be able to see full call stacks.

Some well placed breakpoints - in your WM_PAINT, WM_ERAGEBKGND handlers and you should have a good idea of why your window is being synchronously repainted early during the WM_SIZE cycle.

There are a LOT of windows in the system that consist of a parent window with layered child controls - explorer windows are massivly complicated with listviews, treeviews preview panels etc. Explorer does not have a flicker problem on resizing, so It is celarly possible to get flicker free resizing of parent windows :- what you need to do is catch the repaints, figure out what caused them, and, well, ensure that the cause is removed.

What appears to work:

  1. Use the WS_CLIPCHILDREN on the parent dialog (can be set in WM_INITDIALOG)
  2. During WM_SIZE, loop through the child controls moving and resizing them using DeferSetWindowPos().

This is very close to perfect, in my testing under Windows 7 with Aero.

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