A brilliant idea on how to avoid flickering during control's updating

If you're a Windows Form developer, I think that at least once in your life you've seen the problem of the noisy flickering effect when you've to update a big amount of controls on a form via code.

The common controls that usually needs to be updated (for example by adding lots of data to them) are ListBox, ComboBox, TreeView and ListView, and .NET have two wonderful methods to help this process: BeginUpdate and EndUpdate. This methods are really useful to increase performances on a Winform application and must be always used when you've to add a big amount of data to one of these controls.

The usage is really simple:

Public Sub AddToMyListBox()
    ' Stop the ListBox from drawing while items are added.
    listBox1.BeginUpdate()
      
    ' Loop through and add five thousand new items.
    Dim x As Integer
    For x = 1 To 4999
        listBox1.Items.Add("Item " & x.ToString())
    Next x
    ' End the update process and force a repaint of the ListBox.
    listBox1.EndUpdate()
End Sub

In summary, BeginUpdate maintains performance while items are added to the ListBox (or one of the other controls above) one at a time by preventing the control from drawing until the EndUpdate method is called.

But what happens when you've to update a control that doesn't expose these two methods? Apparently, there's not a real solution to this problem.

Stimulated by this question, Francesco Balena (one that always has brilliant ideas) has found an interesting solution, unfortunately blogged only in italian, but that I think it merits to be published to the entire community.

His idea can be summarized as follow:

  1. Save the aspect of your form pixel by pixel by copying it on a bitmap image
  2. Create a PictureBox control with the size of your form and load the created bitmap into this PictureBox
  3. Add your PictureBox to the Controls collection and bring it to front, in order to cover all the other controls
  4. Update all your controls (using BeginUpdate/EndUpdate methods) while the user is seeing the fixed image of your form (no flickering effect)
  5. When the update operation is completed, delete the PictureBox and show the real form content

The class that you can use to make this work is this:

Public Class FormFreezer
  
Implements IDisposable

   ' The form being frozen
   Dim Form As Form
  
' the auxiliary PictureBox that will cover the form
   Dim PictureBox As PictureBox
  
' the number of times the Freeze method has been called
   Dim FreezeCount As Integer = 0

   ' create an instance associated with a given form
  
' and optionally freeze the form right away
   Public Sub New(ByVal form As Form, Optional ByVal freezeIt As Boolean = False)
      Me.Form = form
      If freezeIt Then Me.Freeze()
   End Sub

   ' freeze the form 
   Public Sub Freeze()
     
' Remember we have frozen the form once more
      FreezeCount += 1
     
' Do nothing if it was already frozen
      If FreezeCount > 1 Then Exit Sub

      ' Create a PictureBox that resizes with its contents
      PictureBox = New PictureBox()
      PictureBox.SizeMode = PictureBoxSizeMode.AutoSize
     
' create a bitmap as large as the form's client area and with same color depth
      Dim frmGraphics As Graphics = Form.CreateGraphics()
      Dim rect As Rectangle = Form.ClientRectangle
      PictureBox.Image = New Bitmap(rect.Width, rect.Height, frmGraphics)
      frmGraphics.Dispose()

      ' copy the screen contents, from the form's client area to the hidden bitmap
      Dim picGraphics As Graphics = Graphics.FromImage(PictureBox.Image)
      picGraphics.CopyFromScreen(Form.PointToScreen(New Point(rect.Left, rect.Top)), New Point(0, 0), New Size(rect.Width, rect.Height))
      picGraphics.Dispose()

      ' Display the bitmap in the picture box, and show the picture box in front of all other controls
      Form.Controls.Add(PictureBox)
      PictureBox.BringToFront()
   End Sub

   ' unfreeze the form
  
' Note: calls to Freeze and Unfreeze must be balanced, unless force=true 
   Public Sub Unfreeze(Optional ByVal force As Boolean = False)
     
' exit if nothing to unfreeze
      If FreezeCount = 0 Then Exit
Sub
     
' remember we've unfrozen the form, but exit if it is still frozen
      FreezeCount -= 1
     
' force the unfreeze if so required
      If force Then FreezeCount = 0
      If FreezeCount > 0 Then Exit Sub

      ' remove the picture box control and clean up
      Form.Controls.Remove(PictureBox)
      PictureBox.Dispose()
      PictureBox =
Nothing
   End Sub

   ' return true if the form is currently frozen
   Public ReadOnly Property IsFrozen() As
Boolean
     
Get
         Return FreezeCount > 0
      End
Get
   End Property

   ' ensure that resources are cleaned up correctly
   Public Overridable Sub Dispose() Implements IDisposable.Dispose
      Me.Unfreeze(True)
   End
Sub
End
Class

This is a nice class with a simple usage. If you have a Windows Form and you need to update its controls as previously described, you can use the code as follow:

   Dim ff As New FormFreezer(Me, True)
  
' update controls here
  
' ...
  
ff.Unfreeze()

Here, Me is your current form, where the controls must be updated.

As pointed by Francesco, the class implements IDisposable so you can use the Using clause and avoid to explicitly call the Unfreeze method, as follow:

   Using New FormFreezer(Me, True)
     
' Update controls here
     
' ...
  
End Using

Calls to Freeze and Unfreeze methods must be balanced (n calls to Freeze requires n calles to Unfreeze at the end).

This is a brilliant idea for a problem that sometimes is really a noise for the end user. Thanks to Francesco for this nice tip and I hope that he permits me to share his ideas to all the "non-Italian" community.

If someone has a more brilliant way to solve this problem, I'm glad to receive suggestions (and I think Francesco will be too).

P.S. the C# version of the class is this:

public class FormFreezer: IDisposable
{

   // The form being frozen

   Form form;

   // the auxiliary PictureBox that will cover the form

   PictureBox pictureBox;

   // the number of times the Freeze method has been called

   int FreezeCount = 0;

 

   // create an instance associated with a given form

   // and freeze the form in base of flag freezeIt

   public FormFreezer(Form form, bool freezeIt)
   {

      this.form = form;

      if (freezeIt) this.Freeze();

   }

 

   // freeze the form 

   public void Freeze()
   {

      // Remember we have frozen the form once more

      // Do nothing if it was already frozen

      if (++FreezeCount > 1) 
         return;

      // Create a PictureBox that resizes with its contents

      pictureBox = new PictureBox();

      pictureBox.SizeMode = PictureBoxSizeMode.AutoSize;

      

      // create a bitmap as large as the form's client area and with same color depth

      Graphics frmGraphics = form.CreateGraphics();

      Rectangle rect = form.ClientRectangle;

      pictureBox.Image = new Bitmap(rect.Width, rect.Height, frmGraphics);

      frmGraphics.Dispose();

 

      // copy the screen contents, from the form's client area to the hidden bitmap

      Graphics picGraphics = Graphics.FromImage(pictureBox.Image);

      picGraphics.CopyFromScreen(form.PointToScreen(new Point(rect.Left, rect.Top)), new Point(0, 0), new Size(rect.Width, rect.Height));

      picGraphics.Dispose();

 

      // Display the bitmap in the picture box, and show the picture box in front of all other controls

      form.Controls.Add(pictureBox);

      pictureBox.BringToFront();

   }

 

   // unfreeze the form

   // Note: calls to Freeze and Unfreeze must be balanced, unless force=true

   public void Unfreeze(bool force)
   {

      // exit if nothing to unfreeze

      if ( FreezeCount == 0 ) 
         return ;

      // remember we've unfrozen the form, but exit if it is still frozen

      FreezeCount -= 1;

      // force the unfreeze if so required

      if (force) 
         FreezeCount = 0;

      if (FreezeCount > 0) 
         return;

      // remove the picture box control and clean up

      pictureBox.Controls.Remove(pictureBox);

      pictureBox.Dispose();

      pictureBox = null;

   }

 

   // return true if the form is currently frozen

   public bool IsFrozen
   {

      get { return (FreezeCount > 0); }

   }

 

   void IDisposable.Dispose()

   {

      this.Unfreeze(true);

   }

}

Print | posted on Thursday, May 04, 2006 11:20 AM

Comments on this post

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
hi Stefano,

thanks for translating and pointing to my post blog in Italian. However, I also have a blog in English and posted this very post there, at this address:

http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,6336502b-57c5-430d-9d37-7f9be484f6b3.aspx
Left by Francesco Balena on May 04, 2006 1:37 PM

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
Francesco, I was missing that you've also an english blog, sorry
However, now I know the link and I think also my readers too
Thanks a lot for this nice tip, having it also here could be useful for a quick retrieval in the future. I'm waiting for other new interesting tips.
Left by Stefano Demiliani on May 04, 2006 1:49 PM

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
hi Stefano,

it's really gr8 atrticle for GDI application. and want help for my application it's also having same fliskring problem on picture box.

in my application we are plotting some real time graph on a picture box control, ans this picture boxbox control is flickering after some time and erase all plotted graph. i want to avoid this flickering.

Thanks n advance

Jitendra
Left by jatin on Dec 28, 2007 9:49 AM

# replica jewelry

Requesting Gravatar...
in my application we are plotting some real time graph on a picture box control, ans this picture boxbox control is flickering after some time and erase all plotted graph. i want to avoid this flickering.
Left by replica jewelry on Apr 29, 2010 4:25 AM

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
are ListBox, ComboBox, TreeView and ListView, and .NET have two wonderful methods to help this process: BeginUpdate and EndUpdate. This methods are really useful to increase performances on a Winform application and must be always used when you've to add a big amou
Left by wholesale laptop adapter on May 23, 2010 2:37 PM

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
pictureBox.Controls.Remove(pictureBox);
should be form.controls.Remove(pictureBox);


Left by Marcus on Dec 08, 2010 11:14 AM

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
in my application we are plotting some real time graph on a picture box control,
Left by clothing manufacturer on Mar 28, 2011 4:49 PM

# re: A brilliant idea on how to avoid flickering during control's updating

Requesting Gravatar...
hi Stefano,

This is grate article, But instead of Creating Bitmap Image I want to display a panel with a specified Color and one label on that panel with some Text suppose "Please Wait....."

Now my Problem is I am able to display the Panel But My label on that panel is not displaying.

Can you please help me out??

Thanks in advance...
Left by Debasish Das on Jun 30, 2011 7:26 AM

Your comment:

 (will show your gravatar)
 
Please add 3 and 2 and type the answer here: