Click here to Skip to main content
15,879,326 members
Articles / Programming Languages / C#
Article

Flicker-free ListView in .NET - Part 2

Rate me:
Please Sign up or sign in to vote.
4.50/5 (19 votes)
3 Feb 20036 min read 243.4K   2.4K   56   27
This article discusses a couple of ways to reduce flicker in the .NET Listview.

Introduction

This is another article about reducing flicker in the ListView. The original article I wrote only works in Windows XP (with manifest file to use Common Controls version 6). You can view it by clicking on this link ListViewXP

This article will focus on two other techniques. Neither of which require Windows XP. Please note that the focus of this article is to dynamically add multiple items with a well-sized image-list without flicker.

WndProc Technique

The first technique involves catching and manipulating messages. As I mentioned in my previous article, every time you add an item to the ListView, the entire control becomes invalidated. When adding multiple items in a loop, the control only paints when the loop ends unless you add Update() or Refresh() (or other various methods) to the end of the loop. Using Update(), Refresh() etc, will cause flickering because the control is constantly invalidated and repainting itself. One way to observe this is to override the WndProc method, and store all messages inside an ArrayList, or another structure. Then when you view the ArrayList, you will see the numbers corresponding to WM_ERASEBKGND and WM_PAINT. We can use this knowledge to help us.

Part of our design of this new class is to allow the programmer to specify that he/she is adding a new item. We create a new method called UpdateItem(int iIndex). The programmer can call this, and it will draw the newly created item only.

C#
public void UpdateItem(int iIndex)
{
    updating = true;
    itemnumber = iIndex;
    this.Update();
    updating = false;
}

First we have a bool private member variable called updating. This will be used in the WndProc method below. When it is set to true, the WndProc method will catch messages. When false, WndProc does nothing except to call base.WndProc(). itemnumber is another newly added private member. It holds the index of the newly added item. Update() is used to redraw invalidated regions of the control (which we play with in WndProc), and finally it sets updating to be false so that messages are no longer being caught by our code. Now we override WndProc in order to make our adjustments.

C#
protected override void WndProc(ref Message messg)
{
    if (updating)  
    {
        // We do not want to erase the background, 
        // turn this message into a null-message
        if ((int)WM.WM_ERASEBKGND == messg.Msg)
            messg.Msg = (int) WM.WM_NULL;
        else if ((int)WM.WM_PAINT == messg.Msg)
        {
            RECT vrect = this.GetWindowRECT();
            // validate the entire window                
            ValidateRect(this.Handle, ref vrect);

            //Invalidate only the new item
            Invalidate(this.Items[itemnumber].Bounds);
        }
    }
    base.WndProc(ref messg);
}

As you can see, we capture the WM_ERASEBKGND message, and we convert it to a NULL message. This will stop the ListView from being erased. WM_PAINT is used to redraw an invalidated area. Since the entire control is invalidated, we need to do a little work. First we Validate the entire viewable area. Doing this will cause WM_PAINT to do nothing, so we follow up by invalidating only the bounds of the new item. Now when painting occurs, it will only occur for the new item because the rest of the control has been validated.

WM_ERASEBKGND, and WM_PAINT are enum'd types representing the int values of the Window messages. You can view these in the code. ValidateRect is a Win32 function that allows us to validate a certain region. Since there is no equivalent in .NET, we import this from user32.dll (see code). RECT is a structure similar to Rectangle, but is needed for the ValidateRect function. When we call ValidateRect, it validates the entire window area of the ListView. We then follow by using the control's Invalidate method to invalidate the bounds of the new item. Now only the new item's bounds are invalidated. This means that only the new item will be painted. Since we captured WM_ERASEBKGND and validated the rest of the control, each item will be drawn as it is added (provided that you use the newly added UpdateItem(index) function). If you don't use UpdateItem(), WndProc will do nothing because "updating" variable will always be false.

NOTE - I'd like to give credit to Carlos H Perez. He does something similar in his ListViewEx control, and part of this idea is based on that work.

Now we can do this (in our main application)

C#
for (int i=0; i < 500; i++)
{
   ListViewItem lvi = 
      new ListViewItem("Item #" + i.ToString(),  indexOfImage);
   listViewFF.Items.Add(lvi);
   listViewFF.UpdateItem(i);
}

Now the items will be painted as they are added to the list view without flicker.

Pure .NET Technique

This next technique is pure .NET and you do not need to extend the ListView class. This technique does not draw the items as they are being added, but describes an alternative way to stop the user from waiting for a long time.

If we do not associate an ImageIndex with a listView item (use -1 as ImageIndex), we can add items to the ListView pretty quickly:

C#
for (int i=0; i < 500; i++)
{
   ListViewItem lvi = 
      new ListViewItem("Item #" + i.ToString(), -1);
   this.listView.Items.Add(lvi);
}

Since we are not calling Update() or Refresh(), we will not see the items as they are being added, but this will be done pretty fast since no images are associated with the ListView.

Now all of the items will be added, so we need to associate an Image with them.

C#
for (int i=0; i < 500; i++)
{
     ListViewItem lvi = this.listView.Items[i] ; 
        // get the already existing item

     lvi.ImageIndex = someNumber ; 
        // Now we associate the item with an image in the imagelist;

     this.listView.Invalidate(lvi.Bounds); 
        // Invalidate the already-existing item
     
    this.Update();  
        // Will force the invalidated region to be redrawn
}

Before this loop begins, all items will be there without an image. Now we simply associate the image, and invalidate the region of the item. Calling Update() will only redraw the invalidated region. In this case, only the bounds of the item will be invalidated, so the image will be drawn to the screen without affecting the other ListView items.

This is good for a thumbnail program. You can add the names of the thumbnails in the first loop, and in the second loop, you can load the bitmaps into the imagelist, associate the already created item with the image index of the newly added image, and now each image will be painted as it's being loaded. What makes it even better is that you can throw this second loop inside of a thread. This will allow the user to scroll the listview while images are being painted.

Advantages/Disadvantages of the techniques

You should use each technique depending on what you need. The WndProc technique allows you to see items as they are being added. If you are adding thousands of items, it may be slower overall, but at least the user can see what's going on. The pure .NET technique will cause the listview to be blank for a few seconds if you try to add (let's say) 5000 items in the first loop, but if the second loop is in a thread, you do not stop the user from doing other things while the images are being drawn.

One thing to note about the WndProc technique. For some reason, it doesn't work very well if you have a manifest file to use the new XP Themes. I'm not sure why, but some items get erased while others are being drawn. I did find a fix for this (and it is used in source code). Before the loop begins set AutoArrange = false for the listview. When the loop ends, you can set it back to true. Of Course, if you're using Windows XP only, you can use the double buffering technique I described in my previous article (See Intro for link to that). If you have no plans to support XP themes, you do not need to mess with the AutoArrange property.

Note about compiling the source

You will need MS Visual Studio .NET to view this project. After unzipping all the files, there will be two directories, ListViewX and TestListViewFF. Open the solution file in ListViewX folder, and after it loads, set the TestListViewFF project to be the startup project. Then when you compile it should be fine.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMultiline TextBox Control Flickering Problem in C# Pin
bishnupatro12-Feb-08 17:05
bishnupatro12-Feb-08 17:05 
Questionthis.Update() returns right away? Pin
Jimmyyy15-May-06 13:52
Jimmyyy15-May-06 13:52 
AnswerRe: this.Update() returns right away? Pin
Jimmyyy21-May-06 10:26
Jimmyyy21-May-06 10:26 
GeneralFlicker Free Pin
caseylara17-Mar-06 10:47
caseylara17-Mar-06 10:47 
GeneralRe: Flicker Free Pin
Testo28ß522-Jul-09 11:05
Testo28ß522-Jul-09 11:05 
QuestionIt's not working Pin
Ricardo Mendes5-Jan-06 1:07
Ricardo Mendes5-Jan-06 1:07 
AnswerRe: It's not working Pin
fputil17-Jan-06 5:27
fputil17-Jan-06 5:27 
GeneralDoubleBuffered Pin
decoder722-Jan-06 4:44
decoder722-Jan-06 4:44 
MSDN advertise that by using .NET 2.0 you can set the Control.DoubleBuffered to true to reduce flickering.

More information here:
http://msdn2.microsoft.com/system.windows.forms.control.doublebuffered.aspx

I'm comming from Borland's VCL world where this property worked pretty well with all VCL controls when was about reducing the flickering.

GeneralRe: DoubleBuffered Pin
xlay24-May-06 11:02
xlay24-May-06 11:02 
GeneralRe: DoubleBuffered Pin
MeNot10-Jul-08 1:17
MeNot10-Jul-08 1:17 
GeneralChanging BackColor/ForeColor Flicker Free Pin
chykun18-Aug-05 15:12
chykun18-Aug-05 15:12 
GeneralNice! Pin
AndrewVos10-Aug-05 10:45
AndrewVos10-Aug-05 10:45 
QuestionHow does it compare with BeginUpdate and EndUpdate Pin
Damien Guard26-Nov-04 0:23
Damien Guard26-Nov-04 0:23 
AnswerRe: How does it compare with BeginUpdate and EndUpdate Pin
rshearer25-Jan-05 10:12
rshearer25-Jan-05 10:12 
GeneralRe: How does it compare with BeginUpdate and EndUpdate Pin
joachimj22-Nov-06 0:45
joachimj22-Nov-06 0:45 
GeneralI found a better(?) technique Pin
JordanBortz22-Jun-03 17:14
JordanBortz22-Jun-03 17:14 
GeneralRe: I found a better(?) technique Pin
Bjørn Reppen6-Jul-03 9:02
sussBjørn Reppen6-Jul-03 9:02 
GeneralIs this technique good only for the list initialization?... Pin
Petru6618-Feb-03 23:51
Petru6618-Feb-03 23:51 
GeneralRe: Is this technique good only for the list initialization?... Pin
Giovanni Montrone19-Feb-03 9:51
Giovanni Montrone19-Feb-03 9:51 
GeneralRe: Is this technique good only for the list initialization?... Pin
Petru6619-Feb-03 23:39
Petru6619-Feb-03 23:39 
GeneralComparison to SuspendLayout() Pin
Christopher Lord5-Feb-03 5:15
Christopher Lord5-Feb-03 5:15 
GeneralRe: Comparison to SuspendLayout() Pin
Giovanni Montrone5-Feb-03 6:29
Giovanni Montrone5-Feb-03 6:29 
GeneralRe: Comparison to SuspendLayout() Pin
Christopher Lord5-Feb-03 11:54
Christopher Lord5-Feb-03 11:54 
GeneralRe: Comparison to SuspendLayout() Pin
Roger Alsing19-Feb-03 0:34
Roger Alsing19-Feb-03 0:34 
GeneralRe: Comparison to SuspendLayout() Pin
mikasa6-Feb-03 6:14
mikasa6-Feb-03 6:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

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