Thursday, August 13, 2009

Reduce Flickering on Windows

On Mac OS X and Linux, windows are double-buffered, meaning everything is drawn to an external source and sent to the screen all at once. On Windows however, changes are drawn to the screen the moment something changes. If you're drawing to a canvas, this can result in a flickering effect that is very annoying.

I've got a few tips to reduce or eliminate flickering:

  1. Turn on the Canvas.DoubleBuffer property.
  2. Turn off EraseBackground. EraseBackground causes the drawing area to be erased first, which will be flushed to the screen immediately causing flicker. This is important for both Canvas and ContainerControl classes. In ContainerControls, make sure the instance has it disabled, and if your code creates one of your ContainerControl subclass "out of thin air" then make sure your subclass has this disabled as well. Keep in mind that sometimes control artifacts can be left behind depending on the type of drawing you're doing, since they weren't erased first. My only suggestion is to try it and see how it works for your case.
  3. Only draw using the Canvas.Paint event. As much as we programmers like to say "Canvas, display this now" using the backdrop property, it is the wrong way to do it. Instead, when you want the canvas to forcefully update, use the refresh method. The backdrop property is only useful from the window editor, when you want to display a static / non-changing graphic.
  4. Whenever you call RectControl.Refresh, pass the optional EraseBackground parameter to it with a false value. This will prevent the control from erasing itself pre-refresh. See tip 2 above.

If you are already double buffering manually, there is no need for tip 1 in your case. In fact, you're better off leaving it disabled if you're going to do the buffering yourself. If your code looks similar to

Dim Buffer As New Picture(G.width,G.height,32)
// do your drawing to Buffer.Graphics

then you're buffering manually. If your paint event has more than a single call to the "G As Graphics" object, then you need to enable the DoubleBuffer property.

Buffering will prevent transparency however, so you might want to fill your area with a color first. This will also help prevent artifacts left over from disabling EraseBackground. Something like:

If Self.TrueWindow.HasBackColor Then
  Buffer.Graphics.ForeColor = Self.TrueWindow.BackColor
  Buffer.Graphics.ForeColor = FillColor

will work well to fill the buffer with the same color as the window it's placed on.

And there you go. Follow these tips, and the flickering effect on Windows will be greatly reduced.


Alexei said...

Thanks for the tips.

What about other contorls such as a TabControl? It flickers quite badly while resized.

Anonymous said...

The problems with flicker have very little to do with "user" controls like custom drawn stuff... we can manage that. The REALproblem is all of the "built-in" controls that look terrible and which we have no control over.

Anonymous said...

is Vista or Windows 7 double buffered?

Thom McGrath said...

Vista is not double-buffered. I'm not sure about 7, I haven't tested it personally, but I don't believe it does either.

Pinus said...

Thanks for these tips Thom. I would add that making an intensive use of XXX.Refresh generates a big machine resources usage, that sometimes provokes high fans rpm and temperature growing. So XXX.Refresh should be used only when it is absolutely needed.

Matt Gardner said...

Our applications developed with RB tend to cause severe flickering on Windows platforms (and along with the usual application crash on exit make the apps look unprofessional to say the least). The same apps on Mac look smooth and professional - so I'd say RealSoftware has quite a bit of work to do to resolve these issues.

I'm not sure Thom has addressed the issue of double-buffering a window (which is what we "draw" into), so the suggestions for resolving flickering are all but useless in real world business apps.

Frankly, RB apps on Windows look terrible.

Alexei said...

So any idea when this issue will be addressed? For me personally flickering in built-in controls is a major showstopper.

Bob Wyttenbach said...

I tried these suggestions on my canvas (a graph that updates when the user changes variables via updown arrows etc.). It didn't help much, but didn't make things any worse either.

I guess what we really need is a DoubleBuffer property for the window itself, not just for canvases. It still surprises me that such a basic thing isn't handled by the OS as it is in Mac and Linux.