c#在更新时闪烁列表视图

时间:2009-01-14 12:44:06

标签: c# listview flicker

我有一个定期更新的列表视图(每60秒)。对我来说很烦人,每当它过时都会闪烁。使用的方法是清除所有项目,然后重新创建它们。我决定不使用新文本清除直接写入单元格的项目。这是一种更好的方法,还是有人有更好的解决方案。

15 个答案:

答案 0 :(得分:88)

ListView控件有一个闪烁问题。问题似乎是控件的Update重载不正确地实现,使其像Refresh一样运行。更新应该使控件仅重绘其无效区域,而刷新重绘控件的整个客户区域。因此,如果您要更改列表中某个项目的背景颜色,则只需要重新绘制该特定项目。不幸的是,ListView控件似乎有不同的意见,并且想要在你弄乱单个项目时重新绘制整个表面......即使当前没有显示该项目。所以,无论如何,您可以通过滚动自己来轻松抑制闪烁,如下所示:

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

来自:Geekswithblogs.net

答案 1 :(得分:22)

除了其他回复之外,许多控件都有一个[Begin|End]Update()方法,可用于在编辑内容时减少闪烁 - 例如:

    listView.BeginUpdate();
    try {
        // listView.Items... (lots of editing)
    } finally {
        listView.EndUpdate();
    }

答案 2 :(得分:5)

答案 3 :(得分:4)

很好的问题和Stormenent的回答很明显。这是他的代码的C ++端口,可供其他任何可能正在处理C ++ / CLI实现的人使用。

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

};

答案 4 :(得分:4)

如果这可以提供帮助,则以下组件解决了我使用.NET 3.5的ListView闪烁问题

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

我将它与.BeginUpdate()和.EndUpdate()方法结合使用,我在那里进行ListView.Items操作。

我不明白为什么这个属性是受保护的...即使在.NET 4.5中(可能是安全问题)

答案 5 :(得分:4)

这是我对C#实现的快速修复,它不需要对列表视图进行子类化等。

使用反射将DoubleBuffered属性设置为在表单构造函数中尝试。

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

答案 6 :(得分:3)

最简单的解决方案可能是使用

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

而不是

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

这种方式更好。

答案 7 :(得分:2)

您可以使用以下扩展程序类将DoubleBuffered属性设置为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);
    }
}

答案 8 :(得分:1)

简单解决方案

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()

答案 9 :(得分:0)

尝试将double buffered属性设置为true。

你也可以使用:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();

答案 10 :(得分:0)

我知道这是一个非常的问题和答案。然而,这是搜索“C ++ / cli listview flicker”时的最佳结果 - 尽管事实上这甚至都不是在讨论C ++。所以这是C ++版本:

我把它放在主表单的头文件中,你可以选择将它放在其他地方......

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

如果你碰巧在这里寻找托管C ++的类似答案,那对我有用。 :)

答案 11 :(得分:0)

在Winrt Windows Phone 8.1中,您可以设置以下代码来解决此问题。

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

答案 12 :(得分:0)

对于它的价值,在我的情况下,我只需要添加一个对

的调用
  

Application.EnableVisualStyles()

在运行应用程序之前,如下所示:

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

否则,双缓冲是不够的。也许这是一个非常古老的项目,默认情况下新的设置......

答案 13 :(得分:0)

这对我来说效果最好。
由于您是直接编辑单元格,因此,最好的解决方案是简单地刷新/重新加载该特定单元格/行,而不是整个表。
您可以使用RedrawItems(...)方法,该方法基本上只重新绘制列表视图中指定范围的项目/行。

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

Reference

对我来说,这完全摆脱了整个列表视图闪烁
在更新时,只有相关的项目/记录闪烁。

干杯!

答案 14 :(得分:0)

如果仍然有人会对此做出回答,我使用了一个计时器稍作延迟,它很好地解决了问题。我想突出显示鼠标移动事件时整行的颜色(更改颜色),但是我认为它可以用于项目替换等。对我来说,listView.BeginUpdate()和listView.EndUpdate()无效,DoubleBuffered属性也无效没用,我在Google上搜索了很多,但没用。

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