Как предотвратить мерцание в ListView при обновлении текста одного ListViewItem?

StackOverflow https://stackoverflow.com/questions/87795

  •  01-07-2019
  •  | 
  •  

Вопрос

Все, что я хочу, это обновить текст 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);
}

Говорят, это решает проблему, но в моем случае это не так..Я считаю, что это потому, что я использую значки на каждом предмете.

Это было полезно?

Решение 2

Чтобы закончить этот вопрос, вот вспомогательный класс, который следует вызывать при загрузке формы для каждого ListView или любого другого производного элемента управления ListView в вашей форме.Спасибо «Брайану Гиллеспи» за решение.

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

Другие советы

Принятый ответ работает, но он довольно длинный, и использование элемента управления (как упоминалось в других ответах) только для включения двойной буферизации также немного перестаралось.Но, к счастью, у нас есть отражение, и мы также можем вызывать внутренние методы, если захотим (но будьте уверены в том, что вы делаете!).

Инкапсулировав этот подход в метод расширения, мы получим довольно короткий класс:

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

ListView в CommonControls 6 (XP или новее) поддерживает двойную буферизацию.К счастью, .NET включает в себя новейшие элементы CommonControl в системе.Чтобы включить двойную буферизацию, отправьте соответствующее сообщение Windows в элемент управления ListView.

Вот подробности:http://www.codeproject.com/KB/list/listviewxp.aspx

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

Я знаю, что этот вопрос довольно старый, но поскольку это один из первых результатов поиска в Google, я хотел бы поделиться своим решением.

Единственный способ убрать мерцание на 100% — это объединить ответ Оливера (класс расширения с двойной буферизацией) и использовать метод BeignUpdate() и EndUpdate() методы.

Ни один из них самостоятельно не смог исправить мерцание.Конечно, я использую очень сложный список, который мне нужно вставлять в список, а также обновлять его почти каждую секунду.

это поможет:

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

Простое решение заключается в следующем:

вашlistview.BeginUpdate()

//Выполняем обновление по добавлению и удалению элемента из списка

вашlistview.EndUpdate()

Если вы хотите обновить только текст, просто установите измененный текст SubItem напрямую, а не обновляйте весь ListViewItem (вы не сказали, как вы делаете обновления).

Показанное вами переопределение эквивалентно простому переопределению OnPaintBackground, что было бы «более правильным» управляемым способом выполнения этой задачи, и оно не поможет ни для одного элемента.

Если у вас все еще есть проблемы, нам понадобятся разъяснения о том, что вы на самом деле пробовали.

Это выстрел в темноте, но вы можете попробовать двойную буферизацию элемента управления.

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

Вызовите метод BeginUpdate() в ListView перед установкой любого элемента представления списка, а затем вызывайте EndUpdate() только после добавления всех элементов.

Это остановит мерцание.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top