it-swarm.com.ru

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

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

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

46
Jonas

Чтобы завершить этот вопрос, вот вспомогательный класс, который должен вызываться при загрузке формы для каждого 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);
    }
}
51
Jonas

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

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

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

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

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

11
Brian Gillespie

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

10
Rolf Kristensen

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

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

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

2
T4cC0re

это поможет:

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

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

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

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

0
ctacke

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

SetStyle(
  ControlStyles.AllPaintingInWmPaint |
  ControlStyles.UserPaint |
  ControlStyles.DoubleBuffer, true)
0
Ed S.

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

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

0
Mike

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

yourlistview.BeginUpdate ()

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

yourlistview.EndUpdate ()

0
jaiveeru