57

I have a list view that is periodically updated (every 60 seconds). It was anoying to me that i would get a flicker every time it up dated. The method being used was to clear all the items and then recreate them. I decided to instead of clearing the items I would just write directly to the cell with the new text. Is this a better approach or does anyone have a better solution.

Majid
  • 13,853
  • 15
  • 77
  • 113
Brad
  • 20,302
  • 36
  • 84
  • 102
  • [MSDN](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listbox.beginupdate?view=netframework-4.8) recommends wrapping bulks of updates in BeginUpdate/EndUpdate. I tested and it helps (the link is on the ListBox, but the same seems to apply for ListView - as in [here](https://stackoverflow.com/a/2196130/1219280)) – Veverke Jul 15 '19 at 09:47

15 Answers15

100

The ListView control has a flicker issue. The problem appears to be that the control's Update overload is improperly implemented such that it acts like a Refresh. An Update should cause the control to redraw only its invalid regions whereas a Refresh redraws the control’s entire client area. So if you were to change, say, the background color of one item in the list then only that particular item should need to be repainted. Unfortunately, the ListView control seems to be of a different opinion and wants to repaint its entire surface whenever you mess with a single item… even if the item is not currently being displayed. So, anyways, you can easily suppress the flicker by rolling your own as follows:

class ListViewNF : System.Windows.Forms.ListView
{
    public ListViewNF()
    {
        //Activate double buffering
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

        //Enable the OnNotifyMessage event so we get a chance to filter out 
        // Windows messages before they get to the form's WndProc
        this.SetStyle(ControlStyles.EnableNotifyMessage, true);
    }

    protected override void OnNotifyMessage(Message m)
    {
        //Filter out the WM_ERASEBKGND message
        if(m.Msg != 0x14)
        {
            base.OnNotifyMessage(m);
        }
    }
}

From: Geekswithblogs.net

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Stormenet
  • 25,926
  • 9
  • 53
  • 65
  • 1
    You have to know that the downside of this solution (due to the double buffering) is that updating the control is significantly slower (try adding a 1000 items). – Sandor Drieënhuizen Jul 09 '10 at 14:04
  • 1
    To be honest, I never use the winforms listview. But isn't that perfomance loss away when you call **SuspendLayout** and **ResumeLayout**? – Stormenet Jul 09 '10 at 15:14
  • 1
    Thank you Stormenent. I have been looking for an solution to this issue for a while and this works well for me. –  May 24 '11 at 08:33
  • 3
    The OptimizedDoubleBuffer does it work properly. But I don't think skipping the call to base.OnNotifyMessage(m) helps in any way. I just checked with ILSpy and Control.OnNotifyMessage has an empty implementation. From MSDN: "When overriding OnNotifyMessage in a derived class, calling the base class's OnNotifyMessage method is not necessary because there is no initial implementation." This method is there so subclasses can be notified when messages are processed. If you need to alter those messages, you need to override WndProc ? – toong Sep 10 '12 at 14:44
  • 4
    If you don't like this solution, because of subclassing, here is very similar without subclassing: http://stackoverflow.com/questions/87795/how-to-prevent-flickering-in-listview-when-updating-a-single-listviewitems-text – prostynick Nov 14 '12 at 13:56
27

In addition to the other replies, many controls have a [Begin|End]Update() method that you can use to reduce flickering when editing the contents - for example:

    listView.BeginUpdate();
    try {
        // listView.Items... (lots of editing)
    } finally {
        listView.EndUpdate();
    }
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
23

Here is my quick fix for a C# implementation that does not require subclassing the list views etc.

Uses reflection to set the DoubleBuffered Property to true in the forms constructor.

    lvMessages
        .GetType()
        .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
        .SetValue(lvMessages, true, null);

Update for 2021: I got pinged on this old post with a comment and I would write this code differently now. Below is an extension method that will add a new method to a ListView to be able to set the double buffered property to true/false as required. This will then extend all list views and make it easier to call as reqired.

/// <summary>
/// Extension methods for List Views
/// </summary>
public static class ListViewExtensions
{
    /// <summary>
    /// Sets the double buffered property of a list view to the specified value
    /// </summary>
    /// <param name="listView">The List view</param>
    /// <param name="doubleBuffered">Double Buffered or not</param>
    public static void SetDoubleBuffered(this System.Windows.Forms.ListView listView, bool doubleBuffered = true)
    {
        listView
            .GetType()
            .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
            .SetValue(listView, doubleBuffered, null);
    }
}
WraithNath
  • 17,658
  • 10
  • 55
  • 82
  • 1
    I think this is by far the simplest solution to implement. Requires no sub classing and allows use of an unmodifed list view from the visual studio tool box. Shame DoubleBuffered is not just exposed as public! – Ashley Duncan Mar 18 '21 at 00:24
  • By far the best solution. Tks – Regis St-Gelais Sep 28 '21 at 17:56
  • 1
    @RegisSt-Gelais Thanks, I have just added an alternate extension method version instead that would make it easier to extend all list views with the same functionality. I wrote the original code a few years ago and I would do it with extension methods these days. – WraithNath Sep 29 '21 at 15:25
  • 1
    @WraithNath It looks a lot to what I coded in my projet :) – Regis St-Gelais Sep 29 '21 at 20:22
  • This should have been the default ListView behavior (DoubleBuffered set to True), its so plain dumb! – demberto May 09 '22 at 09:37
7

If this can help, the following component solved my ListView flickering issues with .NET 3.5

[ToolboxItem(true)]
[ToolboxBitmap(typeof(ListView))]
public class ListViewDoubleBuffered : ListView
{
    public ListViewDoubleBuffered()
    {
        this.DoubleBuffered = true;
    }
}

I use it in conjonction with .BeginUpdate() and .EndUpdate() methods where I do ListView.Items manipulation.

I don't understand why this property is a protected one...even in the .NET 4.5 (maybe a security issue)

5

Excellent question and Stormenent's answer was spot on. Here's a C++ port of his code for anyone else who might be tackling C++/CLI implementations.

#pragma once

#include "Windows.h" // For WM_ERASEBKGND

using namespace System;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

public ref class FlickerFreeListView : public ListView
{
public:
    FlickerFreeListView()
    {
        //Activate double buffering
        SetStyle(ControlStyles::OptimizedDoubleBuffer | ControlStyles::AllPaintingInWmPaint, true);

        //Enable the OnNotifyMessage event so we get a chance to filter out 
        // Windows messages before they get to the form's WndProc
        SetStyle(ControlStyles::EnableNotifyMessage, true);
    }

protected:
    virtual  void OnNotifyMessage(Message m) override
    {
        //Filter out the WM_ERASEBKGND message
        if(m.Msg != WM_ERASEBKGND)
        {
            ListView::OnNotifyMessage(m);
        }
    }

};
Jon Cage
  • 36,366
  • 38
  • 137
  • 215
5

Yes, make it double buffered. It will reduce the flicker ;) http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx

Gonzalo Quero
  • 3,303
  • 18
  • 23
  • 1
    -1: You can't set a `ListView`s DoubleBuffered property (it's protected). – Jon Cage Jul 15 '10 at 14:17
  • 2
    -1 to your comment because you CAN set it, if you derive a new class from it, there is also the "SetStyle" method. But way to do your research before down voting somebody, even when you answered using the exact reason your comment makes no sense *thumbs up* – SilverX Feb 07 '13 at 00:29
  • just remember, SetStyle is protected too... how did you managed to call that? – SilverX Feb 07 '13 at 00:36
4

You can use the following extension class to set the DoubleBuffered property to true:

using System.Reflection;

public static class ListViewExtensions
{
    public static void SetDoubleBuffered(this ListView listView, bool value)
    {
        listView.GetType()
            .GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
            .SetValue(listView, value);
    }
}
Ghost4Man
  • 1,040
  • 1
  • 12
  • 19
3

The simplest Solution would probably be using

       listView.Items.AddRange(listViewItems.ToArray());

instead of

       foreach (ListViewItem listViewItem in listViewItems)
       {
           listView.Items.Add(listViewItem);
       }

This works way better.

Sifty
  • 31
  • 5
  • Thank you so much had real trouble with blocking my complete GUI with just adding items this solved it. – Robin B Feb 15 '18 at 13:37
1

Simple solution

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()
VMAtm
  • 27,943
  • 17
  • 79
  • 125
jaiveeru
  • 95
  • 3
  • This worked for me when I had a listview that I had limited to 100 items, and I was deleting the first row when adding a new row if that threshold was met. – James Love Apr 27 '15 at 09:44
  • Not a useful answer to the question raised because (since edit version 3 way back in 2008) the OP said he was already doing this. – Steve Kidd Apr 17 '19 at 15:44
1

I know this is an extremely old question and answer. However, this is the top result when searching for "C++/cli listview flicker" - despite the fact that this isn't even talking about C++. So here's the C++ version of this:

I put this in the header file for my main form, you can choose to put it elsewhere...

static void DoubleBuffer(Control^ control, bool enable) {
    System::Reflection::PropertyInfo^ info = control->GetType()->
        GetProperty("DoubleBuffered", System::Reflection::BindingFlags::Instance 
            | System::Reflection::BindingFlags::NonPublic);
    info->SetValue(control, enable, nullptr);
}

If you happen to land here looking for a similar answer for managed C++, that works for me. :)

Chris Parker
  • 416
  • 2
  • 9
1

This worked best for me.
Since you are editing the cell directly, the best solution in your case would be to simply refresh/reload that particular cell/row instead of the entire table.
You could use the RedrawItems(...) method that basically repaints only the specified range of items/rows of the listview.

public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly);

Reference

This totally got rid of the full listview flicker for me.
Only the relevant item/record flickers while getting updated.

Cheers!

murphy1310
  • 647
  • 1
  • 6
  • 13
1

If someone would still look an answer for this, I used a timer for a slight delay and it solved the problem nicely. I wanted to highlight (change colour) for the entire row on mouse move event, but I think it would work for item replacement etc. For me listView.BeginUpdate() and listView.EndUpdate() didn't work, DoubleBuffered property also didn't work, I have googled a lot and nothing worked.

private int currentViewItemIndex;
private int lastViewItemIndex;

    private void listView_MouseMove(object sender, MouseEventArgs e)
    {            
        ListViewItem lvi = listView.GetItemAt(e.X, e.Y);

        if (lvi != null && lastViewItemIndex == -1)
        {
            listView.Items[lvi.Index].BackColor = Color.Green;
            lastViewItemIndex = lvi.Index;
        }

        if (lvi != null && lastViewItemIndex != -1)
        {
            currentViewItemIndex = lvi.Index;
            listViewTimer.Start(); 
        } 
    }

    private void listViewTimer_Tick(object sender, EventArgs e)
    {
        listView.BeginUpdate();
        listView.Items[lastViewItemIndex].BackColor = Colour.Transparent;
        listView.Items[currentViewItemIndex].BackColor = Colour.Green;
        listView.EndUpdate();
        lastViewItemIndex = currentViewItemIndex;
        listViewTimer.Stop();
    }
mntt
  • 11
  • 2
0

In Winrt Windows phone 8.1 you can set the following code to fix this issue.

<ListView.ItemContainerTransitions>
    <TransitionCollection/>      
</ListView.ItemContainerTransitions>
Suresh Balaraman
  • 155
  • 1
  • 1
  • 14
0

Try setting the double buffered property in true.

Also you could use:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();
Ironicnet
  • 547
  • 3
  • 14
0

For what it's worth, in my case, I simply had to add a call to

Application.EnableVisualStyles()

before running the application, like this:

    private static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Form1());
    }

Otherwise, double buffering is not enough. Maybe it was a very old project and new ones have that setting by default...

Fred Mauroy
  • 1,219
  • 1
  • 11
  • 10