Resources for visualizing data using C# and the .NET platform
How to improve GUI performance by using two images

If a Bitmap is displayed while it is being drawn on, it may give a flickering appearance. For example, white text moving on a black background may appear to flicker if any frames are shown where the black background is drawn but the text isn’t rendered yet.

Double buffering is a strategy to ensure that only finished images are displayed. This is achieved by using two Bitmap objects, and displaying one while drawing on the other.

Code

In this code example I render double-buffered graphics on a Label. I create two Bitmaps then alternate between them as I draw on the “active” one while displaying the “inactive” one.

Note that I am not drawing on a Picturebox because those are natively double-buffered.

1. Create two Bitmap objects

I start by creating two Bitmap objects sized to my Label, then launch a thread to continuously call my render function

public static Bitmap bmpA;
public static Bitmap bmpB;

public Form2()
{
    InitializeComponent();

    bmpA = new Bitmap(label1.Width, label1.Height);
    bmpB = new Bitmap(label1.Width, label1.Height);

    var t = new Thread(new ThreadStart(RenderContinuously));
    t.Start();
}

2. Display the “Inactive” Bitmap

I use a timer in my Form to trigger display of the inactive bitmap. Notice here that I’m painting directly on the label, not assigning to the Image property like I typically do with Picturebox.

private void timer1_Tick(object sender, EventArgs e)
{
    Bitmap bmpDisplay = (drawingOnA) ? bmpB : bmpA;
    lock (bmpDisplay)
    {
        using (var gfx = label1.CreateGraphics())
            gfx.DrawImage(bmpDisplay, 0, 0);
    }
}

3. Render on the “Active” Bitmap

Rendering is pretty similar to what we’ve seen before. The main difference here is that every time we render we determine which Bitmap is active and lock it. Then, after the render, we flip the bool to swap between active Bitmaps.

public static bool drawingOnA = true;
public static Random rand = new Random();
public static void RenderContinuously()
{
    while (true)
    {
        // determine which Bitmap is the active one
        Bitmap bmpRender = (drawingOnA) ? bmpA : bmpB;
        lock (bmpRender)
        {
            using (var gfx = Graphics.FromImage(bmpRender))
            using (var pen = new Pen(Color.White))
            {
                // draw ten thousand random color anti-aliased lines will be drawn (slow)
                gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                gfx.Clear(Color.Black);
                for (int i = 0; i < 10_000; i++)
                {
                    pen.Color = Color.FromArgb(rand.Next());
                    gfx.DrawLine(pen,
                        rand.Next(bmpRender.Width), rand.Next(bmpRender.Height),
                        rand.Next(bmpRender.Width), rand.Next(bmpRender.Height));
                }
            }

            // Swap active bitmaps
            drawingOnA = !drawingOnA;
        }
    }
}

Notes

  • You can improve the responsiveness of your application by animating graphics without blocking the GUI thread.

  • System.Drawing actually has a BufferedGraphics which may be useful. I frequently choose not to use it because it has more complexity than my applications require.

  • You probably shouldn’t manually double-buffer then assign the Bitmap to a Picturebox (because it’s already double-buffered). However if you do, keep in mind that assigning to a Picturebox’s Image property is different than rendering in that you can’t control when the paint happens, so locking the Bitmap in the timer function isn’t useful. The best solution here is to assign a cloned Bitmap (disposing the previous Bitmap) each time.