51

我想要的只是更新 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);
}

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

4

10 回答 10

76

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

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

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

所有的闪烁都消失了。

更新

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

public static void DoubleBuffered(this Control control, bool enable)
{
    var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
    doubleBufferPropertyInfo.SetValue(control, enable, null);
}
于 2013-03-07T10:11:07.240 回答
52

为了结束这个问题,这里有一个助手类,当为表单中的每个 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);
    }
}
于 2008-10-02T14:55:18.077 回答
11

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

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

于 2008-09-19T17:22:04.757 回答
10

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

于 2010-10-07T23:35:06.020 回答
2

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

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

Neither of those on their own could fix flickering for me. Granted, I use a very complex list, that I need to push into the list and also need to update it almost every sec.

于 2015-04-03T19:58:10.233 回答
1

这将有助于:

class DoubleBufferedListView : System.Windows.Forms.ListView
{
    public DoubleBufferedListView()
        :base()
    {
        this.DoubleBuffered = true;
    }
}
于 2012-04-27T14:26:27.280 回答
0

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

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

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

于 2008-09-17T21:26:40.357 回答
0

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

SetStyle(
  ControlStyles.AllPaintingInWmPaint |
  ControlStyles.UserPaint |
  ControlStyles.DoubleBuffer, true)
于 2008-09-17T21:27:57.827 回答
0

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

这将停止闪烁。

于 2012-09-18T16:08:27.077 回答
0

简单的解决方案是这样的:

yourlistview.BeginUpdate()

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

yourlistview.EndUpdate()

于 2014-08-20T09:51:08.947 回答