How to prevent flickering in ListView when updating a single ListViewItem's text?

C#.NetWinformsListview

C# Problem Overview


All I want is to update an ListViewItem's text whithout seeing any flickering.

This is my code for updating (called several times):

listView.BeginUpdate();
listViewItem.SubItems[0].Text = state.ToString();    // update the state
listViewItem.SubItems[1].Text = progress.ToString(); // update the progress
listView.EndUpdate();

I've seen some solutions that involve overriding the component's WndProc():

protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)WM.WM_ERASEBKGND)
    {
        m.Msg = (int)IntPtr.Zero;
    }
    base.WndProc(ref m);
}

They say it solves the problem, but in my case It didn't. I believe this is because I'm using icons on every item.

C# Solutions


Solution 1 - C#

The accepted answer works, but is quite lengthy, and deriving from the control (like mentioned in the other answers) just to enable double buffering is also a bit overdone. But fortunately we have reflection and can also call internal methods if we like to (but be sure what you do!).

Be encapsulating this approach into an extension method, we'll get a quite short class:

public static class ControlExtensions
{
    public static void DoubleBuffering(this Control control, bool enable)
    {
        var method = typeof(Control).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);
        method.Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer, enable });
    }
}

Which can easily be called within our code:

InitializeComponent();

myListView.DoubleBuffering(true); //after the InitializeComponent();

And all flickering is gone.

Update

I stumbled on this question and due to this fact, the extension method should (maybe) better be:

public static void DoubleBuffered(this Control control, bool enable)
{
	var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
	doubleBufferPropertyInfo.SetValue(control, enable, null);
}

Solution 2 - C#

To end this question, here is a helper class that should be called when the form is loading for each ListView or any other ListView's derived control in your form. Thanks to "Brian Gillespie" for giving the solution.

public enum ListViewExtendedStyles
{
    /// <summary>
    /// LVS_EX_GRIDLINES
    /// </summary>
    GridLines = 0x00000001,
    /// <summary>
    /// LVS_EX_SUBITEMIMAGES
    /// </summary>
    SubItemImages = 0x00000002,
    /// <summary>
    /// LVS_EX_CHECKBOXES
    /// </summary>
    CheckBoxes = 0x00000004,
    /// <summary>
    /// LVS_EX_TRACKSELECT
    /// </summary>
    TrackSelect = 0x00000008,
    /// <summary>
    /// LVS_EX_HEADERDRAGDROP
    /// </summary>
    HeaderDragDrop = 0x00000010,
    /// <summary>
    /// LVS_EX_FULLROWSELECT
    /// </summary>
    FullRowSelect = 0x00000020,
    /// <summary>
    /// LVS_EX_ONECLICKACTIVATE
    /// </summary>
    OneClickActivate = 0x00000040,
    /// <summary>
    /// LVS_EX_TWOCLICKACTIVATE
    /// </summary>
    TwoClickActivate = 0x00000080,
    /// <summary>
    /// LVS_EX_FLATSB
    /// </summary>
    FlatsB = 0x00000100,
    /// <summary>
    /// LVS_EX_REGIONAL
    /// </summary>
    Regional = 0x00000200,
    /// <summary>
    /// LVS_EX_INFOTIP
    /// </summary>
    InfoTip = 0x00000400,
    /// <summary>
    /// LVS_EX_UNDERLINEHOT
    /// </summary>
    UnderlineHot = 0x00000800,
    /// <summary>
    /// LVS_EX_UNDERLINECOLD
    /// </summary>
    UnderlineCold = 0x00001000,
    /// <summary>
    /// LVS_EX_MULTIWORKAREAS
    /// </summary>
    MultilWorkAreas = 0x00002000,
    /// <summary>
    /// LVS_EX_LABELTIP
    /// </summary>
    LabelTip = 0x00004000,
    /// <summary>
    /// LVS_EX_BORDERSELECT
    /// </summary>
    BorderSelect = 0x00008000,
    /// <summary>
    /// LVS_EX_DOUBLEBUFFER
    /// </summary>
    DoubleBuffer = 0x00010000,
    /// <summary>
    /// LVS_EX_HIDELABELS
    /// </summary>
    HideLabels = 0x00020000,
    /// <summary>
    /// LVS_EX_SINGLEROW
    /// </summary>
    SingleRow = 0x00040000,
    /// <summary>
    /// LVS_EX_SNAPTOGRID
    /// </summary>
    SnapToGrid = 0x00080000,
    /// <summary>
    /// LVS_EX_SIMPLESELECT
    /// </summary>
    SimpleSelect = 0x00100000
}

public enum ListViewMessages
{
    First = 0x1000,
    SetExtendedStyle = (First + 54),
    GetExtendedStyle = (First + 55),
}

/// <summary>
/// Contains helper methods to change extended styles on ListView, including enabling double buffering.
/// Based on Giovanni Montrone's article on <see cref="http://www.codeproject.com/KB/list/listviewxp.aspx"/>
/// </summary>
public class ListViewHelper
{
    private ListViewHelper()
    {
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);

    public static void SetExtendedStyle(Control control, ListViewExtendedStyles exStyle)
    {
        ListViewExtendedStyles styles;
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        styles |= exStyle;
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }

    public static void EnableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // enable double buffer and border select
        styles |= ListViewExtendedStyles.DoubleBuffer | ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
    public static void DisableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // disable double buffer and border select
        styles -= styles & ListViewExtendedStyles.DoubleBuffer;
        styles -= styles & ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
}

Solution 3 - C#

The ListView in CommonControls 6 (XP or newer) supports double buffering. Fortunately, .NET wraps the newest CommonControls on the system. To enable double buffering, send the appropriate Windows message to the ListView control.

Here are the details: http://www.codeproject.com/KB/list/listviewxp.aspx

Solution 4 - C#

In .NET Winforms 2.0 there exist a protected property called DoubleBuffered.

By inheriting from ListView, then one can set this protected property to true. This will enable double buffering without needing to call SendMessage.

Setting the DoubleBuffered property is the same as setting the following style:

listview.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94096

Solution 5 - C#

I know this question is quite old, but because this is one of the first search results on Google I wanted to share my fix.

The only way i could remove flickering 100% was to combine the answer from Oliver (extension class with double-buffering) and using the BeignUpdate() and EndUpdate() methods.

Neither of those on their own could fix flickering for me. Granted, I use a very complex list, that I need to push into the list and also need to update it almost every sec.

Solution 6 - C#

this will help:

class DoubleBufferedListView : System.Windows.Forms.ListView
{
    public DoubleBufferedListView()
        :base()
    {
        this.DoubleBuffered = true;
    }
}

Solution 7 - C#

If you only want to update the text, simply set the changed SubItem's text directly rather than updating the entire ListViewItem (you've not said how you're doing your updates).

The override you show is equivalent to simply overriding OnPaintBackground, which would be a "more correct" managed way to do that task, and it's not going to help for a single item.

If you still have problems, we'll need clarification on what you've actually tried.

Solution 8 - C#

This is a shot in the dark, but you could try double buffering the control.

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

Solution 9 - C#

Call the BeginUpdate() method on the ListView before setting any of the list view items and then only call EndUpdate() after all of the items have been added.

That will stop the flicker.

Solution 10 - C#

Simple solution is this:

yourlistview.BeginUpdate()

//Do your update of adding and removing item from the list

yourlistview.EndUpdate()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonasView Question on Stackoverflow
Solution 1 - C#OliverView Answer on Stackoverflow
Solution 2 - C#JonasView Answer on Stackoverflow
Solution 3 - C#Brian GillespieView Answer on Stackoverflow
Solution 4 - C#Rolf KristensenView Answer on Stackoverflow
Solution 5 - C#T4cC0reView Answer on Stackoverflow
Solution 6 - C#BjoernView Answer on Stackoverflow
Solution 7 - C#ctackeView Answer on Stackoverflow
Solution 8 - C#Ed S.View Answer on Stackoverflow
Solution 9 - C#MikeView Answer on Stackoverflow
Solution 10 - C#jaiveeruView Answer on Stackoverflow