c# flickering Listview on update

喜你入骨 提交于 2019-11-27 03:19:34

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

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();
    }

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 try in the forms constructor.

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

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

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)

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);
        }
    }

};

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.

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);
    }
}
jaiveeru

Simple solution

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()

Try setting the double buffered property in true.

Also you could use:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();

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. :)

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

<ListView.ItemContainerTransitions>
    <TransitionCollection/>      
</ListView.ItemContainerTransitions>

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...

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!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!