Click here to Skip to main content
6,936,634 members and growing! (20,324 online)
Email Password   helpLost your password?
 
Multimedia » General Graphics » Graphics     Intermediate License: The Code Project Open License (CPOL)

Don't Flicker! Double Buffer!

By Gil.Schmidt

Demonstrates several ways to overcome the annoying flickering problem.
C#.NET1.1, .NET2.0, WinXPVS.NET2003, VS2005, Dev
Posted:29 Jan 2006
Updated:31 Jan 2006
Views:133,096
Bookmarked:149 times
printPrint Friendly   add Share
      Discuss Discuss   Broken Article?Report  
42 votes for this article.
Popularity: 7.39 Rating: 4.55 out of 5

1
2 votes, 4.8%
2
2 votes, 4.8%
3
8 votes, 19.0%
4
30 votes, 71.4%
5

Introduction

Flickering is a common problem known to everyone who has programmed in the Windows Forms environment. We all know that even the Windows Task Manager flickers when we select a process from the process list.

If you have ever looked around the subject you might have probably noticed that the most common solution to this is Double Buffering.

Explanation

Double Buffer is a technique where we draw all our graphic needs to an image stored in the memory (buffer) and after we are done with all our drawing needs we a draw a complete image from the memory onto the screen. This concentrates the drawing to the screen (an operation that badly effects the performance of the application) to a single operation rather than many small ones.

An easy example to understand this would be to use a ProgressBar that has several layers:

  1. background layer,
  2. border layer,
  3. progress layer,
  4. percent layer.

For each of these layers, we need to call some drawing operation, and after each drawing operation the control redraws itself to the screen. Now, if the refresh rate is low we won't have any problem but if we speed up the refresh rate flickering (blinking) occurs.

We solve this by drawing all the layers to an image that is located in the memory and after drawing all the layers into this image we draw the image onto the screen. This improves the performance dramatically.

Techniques

Note: All of the techniques that are mentioned below are used in the example source code provided with this article except the first one which is from .NET Framework 1.1, and the source code is for .NET Framework 2.0.

Things you should know before we start

  • SetStyle(ControlStyles.AllPaintingInWmPaint, true);

    When a control is painted there are two functions that are called, the OnPaint and the OnPaintBackground. When this flag set, it ignores the OnPaintBackground function and the OnPaint function takes care of drawing both the background and the foreground.

  • SetStyle(ControlStyles.UserPaint, true);

    When this flag is set to true, the control paints itself and is not painted by the system operation.

  • Tip (by Tim McCurdy): SetStyle(ControlStyles.ResizeRedraw, true);

    Setting this flag causes the control to repaint itself when resized.

  • ProgressBar drawing

    For all these examples, I call a function called DrawProgressBar. The parameter passed to it is a Graphics instance that is used for drawing:

    private void DrawProgressBar(Graphics ControlGraphics)
    {
        // draw background
    
        ControlGraphics.FillRectangle(Brushes.Black, ClientRectangle);
        // draw border
    
        ControlGraphics.DrawRectangle(Pens.White, ClientRectangle);
        // draw progress 
    
        ControlGraphics.FillRectangle(Brushes.SkyBlue, 0, 0, 
                this.Width * ProgressBarPercentValue, this.Height);
        // draw percent
    
        ControlGraphics.DrawString(ProgressBarPercentValue.ToString(), 
                           this.Font, Brushes.Red, 
                           new Point(this.Width / 2, this.Height / 2));
    }

Starting off

  • .NET Framework 1.1 built-in double buffer

    public partial class DoubleBufferedControl : Control
    {
        public DoubleBufferedControl()
        {
            InitializeComponent();
    
            this.SetStyle(
                ControlStyles.UserPaint |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.DoubleBuffer, true);
        }
    
        protected override void OnPaint(PaintEventArgs pe)
        {
            // we draw the progressbar normally 
    
            // with the flags sets to our settings
    
            DrawProgressBar(pe.Graphics);
        }
    }

    This technique comes with .NET Framework 1.1 and provides some double buffer support. From what I have tested, this technique is not very good and I prefer using the manual technique for .NET Framework 1.1 (which will be shown later).

  • .NET Framework 2.0 built-in double buffer

    public class DoubleBufferedControl : Control
    {
        public DoubleBufferedControl()
        {
            InitializeComponent();
    
            this.SetStyle(
                ControlStyles.UserPaint |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer, true);
        }
    
        protected override void OnPaint(PaintEventArgs pe)
        {
            // we draw the progressbar normally with 
    
            // the flags sets to our settings
    
            DrawProgressBar(pe.Graphics);
        }
    }

    Well, in .NET Framework 2.0 there is a big improvement in the ease and use of double buffering technique. The performance that we get by using this technique is very good and I recommend this for everyone who doesn't want to get into too much of coding.

    I should mention that when we set Control.DoubleBuffered to true, it will set the ControlStyles.AllPaintingInWmPaint and ControlStyles.OptimizedDoubleBuffer to true.

  • The manual solution for .NET Framework 1.1

    What we do here is create the double buffer ourselves and implement it by overriding the OnPaint event of a control or anything else that you might want to use it on:

    public partial class DoubleBufferedControl : Control
    {
        const Bitmap NO_BACK_BUFFER = null;
        const Graphics NO_BUFFER_GRAPHICS = null;
    
        Bitmap BackBuffer;
        Graphics BufferGraphics;
    
        public DoubleBufferedControl()
        {
            InitializeComponent();
            
            Application.ApplicationExit += 
                new EventHandler(MemoryCleanup);
    
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    
            BackBuffer = new Bitmap(this.Width, this.Height);
            BufferGraphics = Graphics.FromImage(BackBuffer);
        }
    
        private void MemoryCleanup(object sender, EventArgs e)
        {
            // clean up the memory
    
            if (BackBuffer != NO_BACK_BUFFER)
                BackBuffer.Dispose();
    
            if (BufferGraphics != NO_BUFFER_GRAPHICS)
                BufferGraphics.Dispose();
        }
        protected override void OnPaint(PaintEventArgs pe)
        {
            // we draw the progressbar into the image in 
    
            // the memory
    
            DrawProgressBar(BufferGraphics);
    
            // now we draw the image into the screen
    
            pe.Graphics.DrawImageUnscaled(BackBuffer);
        }
    
        private void DoubleBufferedControl_Resize(object sender, 
                                                     EventArgs e)
        {
            if (BackBuffer != NO_BACK_BUFFER)
                BackBuffer.Dispose();
    
            BackBuffer = new Bitmap(this.Width, this.Height);
            BufferGraphics = Graphics.FromImage(BackBuffer);
    
            this.Refresh();
        }
    }
  • The manual solution for .NET Framework 2.0

    In .NET Framework 2.0, we can still use the manual way. Microsoft has provided us with some useful tools to make it even easier. The new tools are BufferedGraphicsContext and BufferedGraphics. BufferedGraphicsContext provides us an alternative buffer instead of the Bitmap that we used in .NET Framework 1.1 and BufferedGraphics handles all the graphics operations like drawing the buffered image to the screen using the Render() function etc:

    public class DoubleBufferedControl : Control
    {
        const BufferedGraphics NO_MANAGED_BACK_BUFFER = null;
    
        BufferedGraphicsContext GraphicManager;
        BufferedGraphics ManagedBackBuffer;
    
        public DoubleBufferedControl()
        {
            InitializeComponent();
            
            Application.ApplicationExit += 
                   new EventHandler(MemoryCleanup);
    
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    
            GraphicManager = BufferedGraphicsManager.Current;
            GraphicManager.MaximumBuffer = 
                   new Size(this.Width + 1, this.Height + 1);
            ManagedBackBuffer = 
                GraphicManager.Allocate(this.CreateGraphics(), 
                                               ClientRectangle);
        }
    
        private void MemoryCleanup(object sender, EventArgs e)
        {
            // clean up the memory
    
            if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
                ManagedBackBuffer.Dispose();
        }
        protected override void OnPaint(PaintEventArgs pe)
        {
            // we draw the progressbar into the image in the memory
    
            DrawProgressBar(ManagedBackBuffer.Graphics);
    
            // now we draw the image into the screen
    
            ManagedBackBuffer.Render(pe.Graphics);
        }
    
        private void DoubleBufferedControl_Resize(object sender, 
                                                      EventArgs e)
        {
            if (ManagedBackBuffer != NO_MANAGED_BACK_BUFFER)
                BackBufferManagedBackBufferDispose();
    
            GraphicManager.MaximumBuffer = 
                  new Size(this.Width + 1, this.Height + 1);
    
            ManagedBackBuffer = 
                GraphicManager.Allocate(this.CreateGraphics(), 
                                                ClientRectangle);
    
            this.Refresh();
        }
    }

Conclusion

Double buffering is a good and simple to use technique that I think anyone who has ever dealt with some graphics programming should know. I am also glad to see that Microsoft has put up lot of time to improve the GUI performance of the .NET Framework and provided us with some better tools to deal with them instead of wasting our time on writing some improvised code.

License

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

About the Author

Gil.Schmidt


Member
learned to prgram in VB when i was 14.
worked in a startup company at the age of 16.
did some open source coding.
learned C# at the age of 21.
did some more open source coding.
worked as a freelancer programmer for 2 years.
currently working as a senior developer in Freedom-UAV.
Occupation: Team Leader
Company: Freedom-UAV
Location: Israel Israel

Other popular General Graphics articles:

  • A flexible charting library for .NET
    Looking for a way to draw 2D line graphs with C#? Here's yet another charting class library with a high degree of configurability, that is also easy to use.
  • CxImage
    CxImage is a C++ class to load, save, display, transform BMP, JPEG, GIF, PNG, TIFF, MNG, ICO, PCX, TGA, WMF, WBMP, JBG, J2K images.
  • 3D Pie Chart
    A class library for drawing 3D pie charts.
  • Barcode Image Generation Library
    This library was designed to give an easy class for developers to use when they need to generate barcode images from a string of data.
  • ImageStone
    An article on a library for image manipulation.
 
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 42 (Total in Forum: 42) (Refresh)FirstPrevNext
GeneralManual solutions and flickering PinmemberHolger Hoffmann4:52 28 Feb '10  
GeneralYES Pinmemberbar49_9#7:39 18 Jan '10  
GeneralGetting parameter Not valid error Pinmemberprashant_1443:03 19 Nov '09  
GeneralProblem in GDI+ drawing Pinmemberskvs22:42 3 Dec '08  
GeneralRe: Problem in GDI+ drawing PinmemberMember 628466023:54 9 Aug '09  
QuestionHow do you double buffer the rendering of all form controls? Not just a single control? Pinmemberjohnnynine219:00 27 Jul '08  
QuestionRe: How do you double buffer the rendering of all form controls? Not just a single control? Pinmemberselin100522:45 2 Sep '08  
AnswerRe: How do you double buffer the rendering of all form controls? Not just a single control? Pinmemberselin100523:26 3 Sep '08  
AnswerRe: How do you double buffer the rendering of all form controls? Not just a single control? PinmemberGil.Schmidt7:23 26 Oct '08  
Questionuse double buffer for picturebox ??? Pinmemberducmanh868:23 15 Nov '07  
AnswerRe: use double buffer for picturebox ??? PinmemberGil.Schmidt0:10 18 Nov '07  
GeneralThank U Pinmemberdr-Wicked3:10 20 Feb '07  
GeneralGarbage Collection & Dispose PinmemberTomer Noy22:31 3 Feb '07  
QuestionHow can make buffer use Metafile! Pinmemberquby5:39 26 Nov '06  
AnswerRe: How can make buffer use Metafile! PinmemberGil_Schmidt7:02 26 Nov '06  
hi man what`s up?

i`ll love to help but right now i`m travelling in south america for few more months, i`ll be back in march. i don`t deal with any programming right now and i must say that i never used Metafile so i need to check it out to get you an answer. but if you can try to check it out and change the code to see if it works with other formats, the thing is that the data is saved in memory maybe some other format will have better performence but i don`t know about the quality, i guess that it would be better to add some quality picker that would improve the performance instead of high quality graphics (for example a simple progress bar doesn`t need really high quality graphics)

well that`s it for now, hasta luego (until later)
GeneralFlicker still seems to exist Pinmemberlevyuk1236:10 31 Oct '06  
GeneralRe: Flicker still seems to exist PinmemberGil_Schmidt6:31 31 Oct '06  
GeneralThanks Pinmemberreinux22:32 4 May '06  
GeneralComposite layered graphics PinmemberJaseNet2:47 4 Mar '06  
GeneralRe: Composite layered graphics PinmemberGil_Schmidt10:40 4 Mar '06  
GeneralRe: Composite layered graphics PinmemberJaseNet2:50 5 Mar '06  
GeneralRe: Composite layered graphics PinmemberGil_Schmidt3:01 5 Mar '06  
GeneralPossible solution? PinmemberITGFanatic8:27 30 Oct '06  
Generalmissing AssemblyInfo.cs from download PinmemberJaseNet2:02 4 Mar '06  
GeneralVery nice article! Pinmemberrippo21:57 7 Feb '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.

PermaLink | Privacy | Terms of Use
Last Updated: 31 Jan 2006
Editor: Rinish Biju
Copyright 2006 by Gil.Schmidt
Everything else Copyright © CodeProject, 1999-2010
Web17 | Advertise on the Code Project