C# 更新时闪烁Listview

发布于 2024-07-11 15:00:34 字数 121 浏览 11 评论 0原文

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

南城追梦 2024-07-18 15:00:35

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

};

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

};
梦在夏天 2024-07-18 15:00:35

您可以使用以下扩展类将 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);
    }
}

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);
    }
}
微暖i 2024-07-18 15:00:35

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

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

而不是

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

这样效果更好。

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.

提赋 2024-07-18 15:00:35

简单的解决方案

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()

Simple solution

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()
夏见 2024-07-18 15:00:35

我知道这是一个极其古老的问题和答案。 然而,这是搜索“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++ 的类似答案,这对我有用。 :)

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

明明#如月 2024-07-18 15:00:35

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

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

参考

这完全消除了我的完整列表视图闪烁
只有相关项目/记录在更新时才会闪烁。

干杯!

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!

寄居者 2024-07-18 15:00:35

如果有人仍然想找到这个问题的答案,我使用了一个计时器来稍微延迟一下,它很好地解决了这个问题。 我想在鼠标移动事件上突出显示(更改颜色)整行,但我认为它适用于项目替换等。对我来说 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();
    }

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();
    }
深者入戏 2024-07-18 15:00:35

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

您也可以使用:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();

Try setting the double buffered property in true.

Also you could use:

this.SuspendLayout();

//update control

this.ResumeLayout(False);

this.PerformLayout();
安稳善良 2024-07-18 15:00:35

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

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

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

<ListView.ItemContainerTransitions>
    <TransitionCollection/>      
</ListView.ItemContainerTransitions>
梦在夏天 2024-07-18 15:00:35

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

Application.EnableVisualStyles()

在运行应用程序之前

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

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

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

帅哥哥的热头脑 2024-07-18 15:00:34

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

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

只为一人 2024-07-18 15:00:34

这是我对 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);
    }
}

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);
    }
}
对不⑦ 2024-07-18 15:00:34

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

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

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();
    }
东京女 2024-07-18 15:00:34

如果这可以帮助,以下组件解决了我在 .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 中(也许是一个安全问题)

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文