【问题标题】:How to prevent flickering in ListView when updating a single ListViewItem's text?更新单个 ListViewItem 的文本时如何防止 ListView 闪烁?
【发布时间】:2010-09-10 09:49:22
【问题描述】:

我只想更新 ListViewItem 的文本而不会看到任何闪烁。

这是我的更新代码(多次调用):

listView.BeginUpdate();
listViewItem.SubItems[0].Text = state.ToString();    // update the state
listViewItem.SubItems[1].Text = progress.ToString(); // update the progress
listView.EndUpdate();

我见过一些涉及覆盖组件的WndProc():的解决方案

protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)WM.WM_ERASEBKGND)
    {
        m.Msg = (int)IntPtr.Zero;
    }
    base.WndProc(ref m);
}

他们说它解决了问题,但在我的情况下它没有。我相信这是因为我在每个项目上都使用了图标。

【问题讨论】:

    标签: c# .net winforms listview


    【解决方案1】:

    接受的答案有效,但相当冗长,并且从控件派生(如其他答案中提到的)只是为了启用双缓冲也有点过头了。但幸运的是,我们有反射,如果我们愿意,也可以调用内部方法(但要确定你做了什么!)。

    将这种方法封装到扩展方法中,我们将得到一个非常短的类:

    public static class ControlExtensions
    {
        public static void DoubleBuffering(this Control control, bool enable)
        {
            var method = typeof(Control).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);
            method.Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer, enable });
        }
    }
    

    可以在我们的代码中轻松调用:

    InitializeComponent();
    
    myListView.DoubleBuffering(true); //after the InitializeComponent();
    

    所有的闪烁都消失了。

    更新

    我偶然发现了this question,由于这个事实,扩展方法应该(也许)更好:

    public static void DoubleBuffered(this Control control, bool enable)
    {
        var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
        doubleBufferPropertyInfo.SetValue(control, enable, null);
    }
    

    【讨论】:

    • 我现在正在使用它(谢谢!),但它看起来很简单,以至于某处必须有一个缺点......
    • 我也会用这个 - 大多数时候反射是一种邪恶的黑客,但这是一个很好的使用它。
    • 注意,这个解决方案非常适合我,直到我在另一台机器上尝试了我的应用程序并出现异常:“无法从程序集 'mscorlib 加载类型'System.Runtime.CompilerServices.ExtensionAttribute' , Version=4.0.0.0, ..." 我猜它与旧的 .NET 版本不兼容。
    • 参数列表中的this关键字将该方法标记为扩展方法。也许您没有在调用类中包含静态类的名称空间。欲了解更多信息,请查看at the documentation
    • @cad - 确保该类不在namespace 中,您将能够直接从Foo.DoubleBuffered(true);等任何控件调用该方法
    【解决方案2】:

    要结束这个问题,这里有一个助手类,当为表单中的每个 ListView 或任何其他 ListView 的派生控件加载表单时,应该调用它。感谢“Brian Gillespie”提供解决方案。

    public enum ListViewExtendedStyles
    {
        /// <summary>
        /// LVS_EX_GRIDLINES
        /// </summary>
        GridLines = 0x00000001,
        /// <summary>
        /// LVS_EX_SUBITEMIMAGES
        /// </summary>
        SubItemImages = 0x00000002,
        /// <summary>
        /// LVS_EX_CHECKBOXES
        /// </summary>
        CheckBoxes = 0x00000004,
        /// <summary>
        /// LVS_EX_TRACKSELECT
        /// </summary>
        TrackSelect = 0x00000008,
        /// <summary>
        /// LVS_EX_HEADERDRAGDROP
        /// </summary>
        HeaderDragDrop = 0x00000010,
        /// <summary>
        /// LVS_EX_FULLROWSELECT
        /// </summary>
        FullRowSelect = 0x00000020,
        /// <summary>
        /// LVS_EX_ONECLICKACTIVATE
        /// </summary>
        OneClickActivate = 0x00000040,
        /// <summary>
        /// LVS_EX_TWOCLICKACTIVATE
        /// </summary>
        TwoClickActivate = 0x00000080,
        /// <summary>
        /// LVS_EX_FLATSB
        /// </summary>
        FlatsB = 0x00000100,
        /// <summary>
        /// LVS_EX_REGIONAL
        /// </summary>
        Regional = 0x00000200,
        /// <summary>
        /// LVS_EX_INFOTIP
        /// </summary>
        InfoTip = 0x00000400,
        /// <summary>
        /// LVS_EX_UNDERLINEHOT
        /// </summary>
        UnderlineHot = 0x00000800,
        /// <summary>
        /// LVS_EX_UNDERLINECOLD
        /// </summary>
        UnderlineCold = 0x00001000,
        /// <summary>
        /// LVS_EX_MULTIWORKAREAS
        /// </summary>
        MultilWorkAreas = 0x00002000,
        /// <summary>
        /// LVS_EX_LABELTIP
        /// </summary>
        LabelTip = 0x00004000,
        /// <summary>
        /// LVS_EX_BORDERSELECT
        /// </summary>
        BorderSelect = 0x00008000,
        /// <summary>
        /// LVS_EX_DOUBLEBUFFER
        /// </summary>
        DoubleBuffer = 0x00010000,
        /// <summary>
        /// LVS_EX_HIDELABELS
        /// </summary>
        HideLabels = 0x00020000,
        /// <summary>
        /// LVS_EX_SINGLEROW
        /// </summary>
        SingleRow = 0x00040000,
        /// <summary>
        /// LVS_EX_SNAPTOGRID
        /// </summary>
        SnapToGrid = 0x00080000,
        /// <summary>
        /// LVS_EX_SIMPLESELECT
        /// </summary>
        SimpleSelect = 0x00100000
    }
    
    public enum ListViewMessages
    {
        First = 0x1000,
        SetExtendedStyle = (First + 54),
        GetExtendedStyle = (First + 55),
    }
    
    /// <summary>
    /// Contains helper methods to change extended styles on ListView, including enabling double buffering.
    /// Based on Giovanni Montrone's article on <see cref="http://www.codeproject.com/KB/list/listviewxp.aspx"/>
    /// </summary>
    public class ListViewHelper
    {
        private ListViewHelper()
        {
        }
    
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);
    
        public static void SetExtendedStyle(Control control, ListViewExtendedStyles exStyle)
        {
            ListViewExtendedStyles styles;
            styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
            styles |= exStyle;
            SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
        }
    
        public static void EnableDoubleBuffer(Control control)
        {
            ListViewExtendedStyles styles;
            // read current style
            styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
            // enable double buffer and border select
            styles |= ListViewExtendedStyles.DoubleBuffer | ListViewExtendedStyles.BorderSelect;
            // write new style
            SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
        }
        public static void DisableDoubleBuffer(Control control)
        {
            ListViewExtendedStyles styles;
            // read current style
            styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
            // disable double buffer and border select
            styles -= styles & ListViewExtendedStyles.DoubleBuffer;
            styles -= styles & ListViewExtendedStyles.BorderSelect;
            // write new style
            SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
        }
    }
    

    【讨论】:

    • 应该注意的是,这只有在被覆盖的方法“OnHandleCreated”中设置时才有效。在 ctor 中设置它是行不通的。使用带有标记 OptimizedDoubleBuffer 的 SetStyle() 将在 ctor 中工作!
    【解决方案3】:

    CommonControls 6(XP 或更新版本)中的 ListView 支持双缓冲。幸运的是,.NET 在系统上封装了最新的 CommonControls。要启用双缓冲,请将适当的 Windows 消息发送到 ListView 控件。

    以下是详细信息: http://www.codeproject.com/KB/list/listviewxp.aspx

    【讨论】:

      【解决方案4】:

      在 .NET Winforms 2.0 中存在一个名为 DoubleBuffered 的受保护属性。

      通过从 ListView 继承,可以将此受保护的属性设置为 true。这将启用双缓冲,而无需调用 SendMessage。

      设置 DoubleBuffered 属性与设置如下样式相同:

      listview.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
      

      http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94096

      【讨论】:

      • 很遗憾,SetStyle 也受到保护。
      【解决方案5】:

      我知道这个问题已经很老了,但因为这是 Google 上的第一批搜索结果之一,所以我想分享我的解决方法。

      我可以 100% 消除闪烁的唯一方法是结合 Oliver 的答案(带有双缓冲的扩展类)并使用 BeignUpdate()EndUpdate() 方法。

      这些都无法为我解决闪烁问题。 当然,我使用了一个非常复杂的列表,我需要将其推送到列表中,并且几乎每秒钟都需要更新它。

      【讨论】:

        【解决方案6】:

        这会有所帮助:

        class DoubleBufferedListView : System.Windows.Forms.ListView
        {
            public DoubleBufferedListView()
                :base()
            {
                this.DoubleBuffered = true;
            }
        }
        

        【讨论】:

          【解决方案7】:

          如果您只想更新文本,只需直接设置更改后的 SubItem 的文本,而不是更新整个 ListViewItem(您还没有说您是如何进行更新的)。

          您显示的覆盖等同于简单地覆盖 OnPaintBackground,这将是一种“更正确”的托管方式来完成该任务,并且对单个项目没有帮助。

          如果您仍有问题,我们需要说明您实际尝试过的内容。

          【讨论】:

            【解决方案8】:

            这是在黑暗中拍摄的,但您可以尝试对控件进行双重缓冲。

            SetStyle(
              ControlStyles.AllPaintingInWmPaint |
              ControlStyles.UserPaint |
              ControlStyles.DoubleBuffer, true)
            

            【讨论】:

              【解决方案9】:

              在设置任何列表视图项之前调用 ListView 上的 BeginUpdate() 方法,然后仅在添加所有项后调用 EndUpdate()。

              这将停止闪烁。

              【讨论】:

                【解决方案10】:

                简单的解决办法是这样的:

                yourlistview.BeginUpdate()

                //从列表中添加和删除项目的更新

                你的列表视图.EndUpdate()

                【讨论】:

                • 在我的代码中执行上述操作并不能解决闪烁问题。打开双缓冲确实如此。
                猜你喜欢
                • 2015-02-25
                • 1970-01-01
                • 2014-01-06
                • 2013-08-04
                • 2015-03-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多