55

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

4

15 回答 15

98

ListView 控件存在闪烁问题。问题似乎是控件的更新重载未正确实现,因此它的行为类似于刷新。更新应使控件仅重绘其无效区域,而刷新则重绘控件的整个客户区。因此,如果您要更改列表中一项的背景颜色,那么只需重新绘制该特定项。不幸的是,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

于 2009-01-14T12:47:52.920 回答
26

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

    listView.BeginUpdate();
    try {
        // listView.Items... (lots of editing)
    } finally {
        listView.EndUpdate();
    }
于 2009-01-14T12:54:34.360 回答
18

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

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

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

2021 年更新:我在这篇旧帖子上收到了一条评论,我现在会以不同的方式编写这段代码。下面是一个扩展方法,它将向 ListView 添加一个新方法,以便能够根据需要将双缓冲属性设置为 true/false。然后,这将扩展所有列表视图并使其更容易按要求调用。

/// <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);
    }
}
于 2017-02-22T11:10:28.843 回答
7

如果这有帮助,以下组件解决了我的 .NET 3.5 的 ListView 闪烁问题

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

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

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

于 2012-12-17T12:20:16.510 回答
5

是的,让它双缓冲。它将减少闪烁;) http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx

于 2009-01-14T12:48:56.303 回答
5

很好的问题,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);
        }
    }

};
于 2010-07-15T14:14:11.880 回答
4

您可以使用以下扩展类将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);
    }
}
于 2018-04-27T13:26:47.857 回答
3

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

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

代替

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

这效果更好。

于 2015-05-08T18:22:20.717 回答
1

简单的解决方案

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()
于 2014-08-20T10:06:17.777 回答
1

我知道这是一个非常古老的问题和答案。但是,这是搜索“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++ 寻找类似的答案,那对我有用。:)

于 2016-10-27T19:42:44.103 回答
1

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

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

参考

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

干杯!

于 2019-07-31T15:44:44.297 回答
0

尝试将双缓冲属性设置为 true。

你也可以使用:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();
于 2009-01-14T12:53:30.243 回答
0

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

<ListView.ItemContainerTransitions>
    <TransitionCollection/>      
</ListView.ItemContainerTransitions>
于 2016-11-28T05:41:38.303 回答
0

对于它的价值,就我而言,我只需要添加一个调用

Application.EnableVisualStyles()

在运行应用程序之前,像这样:

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

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

于 2018-04-28T17:56:36.567 回答
0

如果有人仍然会为此寻找答案,我使用了一个计时器稍微延迟,它很好地解决了这个问题。我想在鼠标移动事件上突出显示(更改颜色)整行,但我认为它适用于项目替换等。对我来说 listView.BeginUpdate() 和 listView.EndUpdate() 不起作用,DoubleBuffered 属性也没有不工作,我用谷歌搜索了很多,但没有任何工作。

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();
    }
于 2020-12-17T22:19:22.537 回答