Click here to Skip to main content
15,897,891 members
Articles / Programming Languages / C#

Don't Flicker! Double Buffer!

Rate me:
Please Sign up or sign in to vote.
4.83/5 (57 votes)
31 Jan 2006CPOL4 min read 425.6K   13.4K   210  
Demonstrates several ways to overcome the annoying flickering problem
In this article, you will see how double buffering is a good and simple to use technique that helps overcome the flickering problem.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace DoubleBufferExample
{
	public class DoubleBufferControl : Control
	{
        const Graphics NO_BUFFER_GRAPHICS = null;
        const Bitmap NO_BACK_BUFFER = null;
        const BufferedGraphics NO_MANAGED_BACK_BUFFER = null;

        Bitmap BackBuffer;
        Graphics BufferGraphics;
        BufferedGraphicsContext GraphicManager;
        BufferedGraphics ManagedBackBuffer;
		
		DoubleBufferMethod _PaintMethod = DoubleBufferMethod.NoDoubleBuffer;
		GraphicTestMethods _GraphicTest = GraphicTestMethods.DrawTest;

        public DoubleBufferControl()
        {
            InitializeComponent();

            SetStyle(
                ControlStyles.AllPaintingInWmPaint | 
                ControlStyles.UserPaint, true);

            Application.ApplicationExit += new EventHandler(MemoryCleanup);
        }

        private void InitializeComponent()
		{
            this.SuspendLayout();
            // 
            // DoubleBufferControl
            // 
            this.BackColor = System.Drawing.Color.Black;
            this.Resize += new System.EventHandler(this.DoubleBufferControl_Resize);
            this.ResumeLayout(false);

		}

		public DoubleBufferMethod PaintMethod
		{
			get { return _PaintMethod; }
			set
			{
				_PaintMethod = value;
				RemovePaintMethods();

                switch (value)
                {
                    case DoubleBufferMethod.BuiltInDoubleBuffer:
                        this.SetStyle(ControlStyles.UserPaint, true);
                        this.DoubleBuffered = true;
                        break;
                    case DoubleBufferMethod.BuiltInOptimizedDoubleBuffer:
                        this.SetStyle(
                            ControlStyles.OptimizedDoubleBuffer | 
                            ControlStyles.AllPaintingInWmPaint, true);
                        break;
                    case DoubleBufferMethod.ManualDoubleBuffer11:
                        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                        BackBuffer = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
                        BufferGraphics = Graphics.FromImage(BackBuffer);
                        break;

                    case DoubleBufferMethod.ManualDoubleBuffer20:
                        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                        GraphicManager = BufferedGraphicsManager.Current;
                        GraphicManager.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
                        ManagedBackBuffer = GraphicManager.Allocate(this.CreateGraphics(), ClientRectangle);
                        break;
                }
			}
		}

		public GraphicTestMethods GraphicTest
		{
			get { return _GraphicTest; }
			set	
			{
				this.CreateGraphics().Clear(Color.Wheat);
				_GraphicTest = value;	
			}
		}

		public enum DoubleBufferMethod
		{
			NoDoubleBuffer,
			BuiltInDoubleBuffer,
			BuiltInOptimizedDoubleBuffer,
			ManualDoubleBuffer11,
			ManualDoubleBuffer20
		};

		public enum GraphicTestMethods
		{
			DrawTest,
			FillTest
		};

		private void MemoryCleanup(object sender, EventArgs e)
		{
            if (BufferGraphics != NO_BUFFER_GRAPHICS)
                BufferGraphics.Dispose();

			if (BackBuffer != NO_BACK_BUFFER)
				BackBuffer.Dispose();

            if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
                ManagedBackBuffer.Dispose();
		}


		protected override void OnPaint(PaintEventArgs e)
		{
            if (DesignMode) { base.OnPaint(e); return; }

			switch (_PaintMethod)
			{
				case DoubleBufferMethod.NoDoubleBuffer:
					base.OnPaint(e);
					LunchGraphicTest(e.Graphics);
					break;

				case DoubleBufferMethod.BuiltInDoubleBuffer:
					LunchGraphicTest(e.Graphics);
					break;

				case DoubleBufferMethod.BuiltInOptimizedDoubleBuffer:
					LunchGraphicTest(e.Graphics); 
					break;

				case DoubleBufferMethod.ManualDoubleBuffer11:
					PaintDoubleBuffer11(e.Graphics); break;

				case DoubleBufferMethod.ManualDoubleBuffer20:
					PaintDoubleBuffer20(e.Graphics); break;
			}
		}

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
/*            if (_PaintMethod != DoubleBufferMethod.ManualDoubleBuffer11 &&
                _PaintMethod != DoubleBufferMethod.ManualDoubleBuffer20)

                base.OnPaintBackground(pevent);*/
        }

		private void RemovePaintMethods()
		{
			this.DoubleBuffered = false;

			this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);

            if (BufferGraphics != NO_BUFFER_GRAPHICS)
            {
                BufferGraphics.Dispose();
                BufferGraphics = NO_BUFFER_GRAPHICS;
            }

            if (BackBuffer != NO_BACK_BUFFER)
            {
                BackBuffer.Dispose();
                BackBuffer = NO_BACK_BUFFER;
            }

            if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
                ManagedBackBuffer.Dispose();
		}

        private void DoubleBufferControl_Resize(object sender, EventArgs e)
        {
            switch (_PaintMethod)
            {
                case DoubleBufferMethod.ManualDoubleBuffer11:
                    BackBuffer = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
                    BufferGraphics = Graphics.FromImage(BackBuffer);
                    break;

                case DoubleBufferMethod.ManualDoubleBuffer20:
                    GraphicManager.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);

                    if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
                        ManagedBackBuffer.Dispose();

                    ManagedBackBuffer = GraphicManager.Allocate(this.CreateGraphics(), ClientRectangle);
                    break;
            }

            this.Refresh();
        }

        private void PaintDoubleBuffer11(Graphics ControlGraphics)
        {
            LunchGraphicTest(BufferGraphics);

            // this draws the image from the buffer into the form area 
            // (note: DrawImageUnscaled is the fastest way)
            ControlGraphics.DrawImageUnscaled(BackBuffer, 0, 0); 
        }

		private void PaintDoubleBuffer20(Graphics ControlGraphics)
		{
			try
			{
				LunchGraphicTest(ManagedBackBuffer.Graphics);
                
                // paint the picture in from the back buffer into the form draw area
                ManagedBackBuffer.Render(ControlGraphics);
			}
			catch (Exception Exp) { Console.WriteLine(Exp.Message); }
		}

		private void LunchGraphicTest(Graphics TempGraphics)
		{
			int i;
			Random Rnd = new Random();
			Pen BlackPen = new Pen(new SolidBrush(Color.Black));
            Pen ColorPen = null;
			Rectangle TempRectangle;
            LinearGradientBrush ColorBrush = null;

			TempGraphics.Clear(Color.Wheat);

			switch (GraphicTest)
			{
				case GraphicTestMethods.DrawTest:
					for (i = 0; i < 100; i++)
					{
						TempRectangle = new Rectangle(
							Rnd.Next(0, Width),
							Rnd.Next(0, Height),
							Width - i,
							Height - i);

						ColorPen = new Pen(Color.FromArgb(127, Rnd.Next(0, 256), Rnd.Next(256), Rnd.Next(256)));
                        TempGraphics.DrawRectangle(ColorPen, TempRectangle);
					}
                    
                    ColorPen.Dispose();
					break;

				case GraphicTestMethods.FillTest:
					for (i = 0; i < 100; i++)
					{
						TempRectangle = new Rectangle(
							Rnd.Next(0, Width),
							Rnd.Next(0, Height),
							Width - i,
							Height - i);

                        ColorBrush = new LinearGradientBrush(
                                TempRectangle,
                                Color.FromArgb(127, Rnd.Next(0, 256), Rnd.Next(256), Rnd.Next(256)),
                                Color.FromArgb(127, Rnd.Next(0, 256), Rnd.Next(256), Rnd.Next(256)),
                                (LinearGradientMode)Rnd.Next(3));

                        TempGraphics.FillEllipse(ColorBrush, TempRectangle);

					}

                    ColorBrush.Dispose();
					break;
			}
		}
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions