I have seen a few different ways to achieve
100% flicker free rendering in custom controls. Some work and some don't. Here
is a method that I have used on my projects to provide my clients with
beautiful, easy to use controls.
The way we achieve this is we draw both the background and the foreground to a
buffer, and then draw that buffer to the screen in one go. Its a very simple
technique that only requires a bitmap to use as a buffer, and you override three
methods, OnResize, OnPaintBackgroundand OnPaint.
First we need the buffer, which should be a class variable as such
private
Bitmap buffer = null;
The buffer only needs to be rebuilt when the control is created and when its
resized. This is why we overload the OnResize method. Here we will dispose of
the current buffer if needed and create the new with the current controls size,
but only if both the width and height are greater than 0, otherwise it will
throw an error if it tries.
protected
override void
OnResize(EventArgs e)
{
base.OnResize(e);
if (this.Width
> 0 && this.Height > 0)
{
if (this.buffer
!= null)
this.buffer.Dispose();
this.buffer =
new Bitmap(this.Width,
this.Height);
}
// make sure it redraws on resize
this.Invalidate();
}
The OnResize method will be called before the control is shown, so this will
initialise the buffer as well.
Painting the background of the control is done in the OnPaintBackground method
of course. This is where most of the flickering will occur as it tries to clear
the control first. We don't want it to do this, but if we simply override the
method and don't call the base implementation then the control will not be
rendered correctly. So instead we discard the provided PaintEventArgs, and pass
in one of our own.
protected
override void
OnPaintBackground(PaintEventArgs e)
{
Graphics g
= Graphics.FromImage(buffer);
PaintEventArgs ex = new PaintEventArgs(g,
e.ClipRectangle);
base.OnPaintBackground(ex);
}
Now the control will paint the background to our buffer instead of directly to
the screen. The only thing left to do is to do your custom drawing, which can
all be done in the OnPaint method with a little extra code to display the buffer
at the end.
protected
override void
OnPaint(PaintEventArgs e)
{
Graphics gB
= Graphics.FromImage(buffer);
// do any drawing you need to here to gB
// the clipping on the provided graphics object can
create
// some refresh artefacts so I always use a new
graphics object
Graphics gM
= this.CreateGraphics();
gM.DrawImage(buffer, new Point(0, 0));
gM.Dispose();
gB.Dispose();
}
All of your drawing is still done on the buffer, and once your finished you draw
the buffer on the control using a new instance of its Graphics object with the
DrawImage method.
I hope this helps you create more professional looking controls and interfaces.
For a working example, please find the scrollable label at my blog
www.code-dragon.com which includes a test form you can play around with.