Thursday 17 May 2007

double buffering in C#

Don't do anything other than copy a back buffer to the control in the controls paint method.

Don't necessarily call Refresh() from event handlers that modify the image -- it's inefficient to call Refresh from a mouse movement handler that handles dragging of an object across the control, for example. Draw on a back buffer then Refresh() at set points in time (<= 250 ms) using a System.Windows.Forms.Timer.

Code snippets here as a reminder:

private System.Windows.Forms.Timer timer;
private BufferedGraphicsContext context;
private BufferedGraphics gfx;
private System.Windows.Forms.Panel panel1;

public Form()
{
this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1Paint);

this.SetStyle(ControlStyles.AllPaintingInWmPaint  ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

context = BufferedGraphicsManager.Current;
context.MaximumBuffer = new Size(this.panel1.Width + 1, this.panel1.Height + 1);

gfx = context.Allocate(this.panel1.CreateGraphics(),
new Rectangle(0, 0, this.panel1.Width, this.panel1.Height));

timer = new System.Windows.Forms.Timer();
timer.Interval = 100;
timer.Tick += new EventHandler(this.OnTimer);
timer.Start();
}

private void OnTimer(object sender, EventArgs e)
{
DrawToBuffer(gfx.Graphics);
gfx.Render(Graphics.FromHwnd(this.panel1.Handle));
}

void panel1Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
gfx.Render();
}

private void panel1Resize(object sender, EventArgs e)
{
context.MaximumBuffer = new Size(this.panel1.Width + 1, this.panel1.Height + 1);
if (gfx != null)
{
gfx.Dispose();
gfx = null;
}
gfx = context.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.panel1.Width, this.panel1.Height));
DrawToBuffer(gfx.Graphics);
this.Refresh();
}