Jeff Kryzer wrote, on the DOTNET-CLR mailing list, about how to supress flicker in your controls. Since this comes in pretty handy when using Direct3D to write controls, and since I get a fair amount of traffic on this site now looking for Direct3D info, I thought I'd replicate his excellent post here. You can find the original thread here .
I agree with Ian. I had much flickering on my control. After messing with some events and styles, I got it smooth as silk. First, do you have all the following styles set?
this.SetStyle ( ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.Opaque , true);
If you don't, then you can make it more efficient. Allow me to explain why your control flickers (I learned this the hard way). EVERY time your control paints, there are 2 events fired. OnPaintBackground and then OnPaint. They always fire one after another. So as a user types, they will fire like this: OnPaintBackground, OnPaint, OnPaintBackground, OnPaint, etc.
The flicker is caused by the OnPaintBackground painting the entire control all white (or gray or whatever your backcolor is). Then the Paint immediately follows this by painting your grid. Which overwrites your white background. Then OnPaintBackground fires again and overwrites your entire grid with a white sheet again. This cycle is what causes the flicker. To PROVE this, simply add the following code to your control:
protected override void OnPaintBackground ( System.Windows.Forms.PaintEventArgs pevent ) { //do nothing }
This will eliminate the white sheet painting. Your painting will now be smooth and you shouldn't see any flicker. Now the only drawback is that if there is an area at the bottom of your grid which isn't being used, you see garbage in that area (like if your grid shows 10 lines, but youonly have 3 rows currently showing). To counteract this "see through/garbage" area of the control, you simply clear the entire control in the OnPaint method, and don't let the OnPaintBackground fire. Note that setting the AllPaintingInWmPaint Style is the same as overriding the OnPaintBackground method and doing nothing (like above code).
You need to set the ControlStyles.Opaque bit to prevent OnPaintBackground from being called._
So now you might be asking, how is it that I can clear the background in the OnPaint method, and there is no flicker as there is when doing it in OnPaintBackground? This is where the DoubleBuffer comes in and eliminates that.
The BeginUpdate and EndUpdate as you suggest is already implemented. It looks like this:
this.BeginUpdate () this.OnPaint () this.EndUpdate ()
That is, the above code is what happens when DoubleBuffering is turned on. It says, suspend all drawing, I'm about to start the OnPaint method. Then when EndUpdate is called, it says, OK, paint everything all at once since BeginUpdate was last called. THIS is why you won't see a flicker when the white sheet is painted in the OnPaint method.
However, the commentary isn't quite right.
If you set ControlStyles.AllPaintingInWmPaint, then the default OnPaint method actually calls OnPaintBackground() for you (if you remember to call base.OnPaint () in your override). If you then enable ControlStyles.DoubleBuffer as well you, get both the background and the foreground paint performed on the back buffer.
This allows us to retain the benefit of being able to separate background from foreground painting (for ease of decoration), whilst getting shot of the egregious two-message legacy cruft of WM_PAINT and WM_ERASEBKGND.
So just to reinforce this, when no styles are set, your controls code looks like this behind the scenes:
this.OnPaintBackground() this.OnPaint()
If you turn DoubleBuffer on, it looks like this:
this.OnPaintBackground() this.BeginUpdate() this.OnPaint() this.EndUpdate()
And if you turn on the AllPaintingInWmPaint style, it looks like this:
this.BeginUpdate () this.OnPaint () this.EndUpdate ()
And this, my friend, is the most efficient way of painting your control without flicking.
If you use the AllPaintingInWmPaint attribute, system.drawing.dll will sometimes throw a System.ArgumentException claiming that you have used an Invalid Parameter. This happens if you follow the generally recommended procedure of disposing of the Graphics object at the end of the OnPainthandler, either explicitly with
g.Dispose()
or by using the construct:
using (g = e.Graphics) { ... }
To prevent this, declare and reference a persistant Graphics object as a class member.