Мерцание в элементе управления ListView (OwnerDraw, Virtual)

Этот вопрос можно рассматривать как продолжение мерцания в режиме просмотра списка с помощью ownerdraw и виртуальный режим.

У меня ListView элемент управления в Virtual mode, и я пытаюсь выполнить собственный рисунок. Отрисовка элемента выполняется с помощью следующего переопределения метода:

protected override void OnDrawItem(DrawListViewItemEventArgs eventArgs) 

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


Теперь - принятый ответ на упомянутый вопрос говорит нам:

Это ошибка в ListView .NET, и вы не можете обойти ее с помощью двойной буферизации.

  • Итак, насколько надежна эта информация? Это действительно ошибка? Или, может быть, мы просто пытаемся отрезать часть сообщений и надеяться, что это не изменит видимое поведение?

  • Верно ли, что если у меня есть процедура рисования моего владельца для ListView в Virtual Mode,, я могу подавить эти Custom Draw события и выполнять рисование только в WM_PAINT, или, может быть, это неверно для некоторых случаев?

  • Каковы предварительные условия для того, чтобы элемент управления System.Windows.Forms мог выполнять всю отрисовку в WM_PAINT без изменения его первоначального поведения?


person Yippie-Ki-Yay    schedule 07.05.2012    source источник
comment
Образец MSDN - отстой. Внутри есть несколько ошибок. Я решил их, переопределив WndProc и отфильтровав определенные сообщения NMHDR, которые связаны с горячим отслеживанием и другими вещами. У меня сейчас нет кода, но я могу предоставить образец, который решает проблемы, с которыми вы также столкнулись.   -  person Alois Kraus    schedule 13.05.2012


Ответы (3)


Я видел эту мерцающую проблему с элементом управления ListView в любых обработчиках пользовательских событий рендеринга (DrawItem, DrawSubItem). Я безуспешно пробовал BeginUpdate () / EndUpdate () и двойную буферизацию. Я думаю, что .NET запускает дополнительный WM_PAINT для всех столбцов, находящихся справа от настраиваемого нарисованного столбца.

Однако я нашел этот обходной путь для одного настраиваемого столбца ListView. Работает отлично!

  1. В редакторе заголовка столбца установите пользовательский нарисованный столбец как последний столбец.
  2. Измените DisplayIndex нужной позиции.

Это должно решить проблему мерцания при наведении курсора мыши или рендеринге во время выполнения.

person detale    schedule 14.05.2012

По крайней мере, для двойной буферизации для OnDrawItem неверно, что есть ошибка, но это немного глупо: есть защищенный атрибут, который вы можете установить, но вам нужно переопределить ListView. Я создал такой класс:

public class MyListView : ListView
{
    public MyListView()
        : base()
    {
        DoubleBuffered = true;
    }
}

А затем в моем файле MyForm.Designer.cs я изменяю экземпляр ListView следующей строкой:

private ListView myListView;

this.myListView = new MyListView();

И OnDrawItem будет работать как шарм!

person zmilojko    schedule 08.05.2012
comment
Установка атрибута DoubleBuffered не устраняет мерцание мыши над первым столбцом, потому что рисование в этом случае фактически происходит вне обработки WM_PAINT. Копаясь в коде, я обнаружил, что отправленные события рисования на самом деле являются поддельными и используются только для определения размера метки (sic!). Ну, по крайней мере, так кажется (я отлаживал user32.dll библиотеку с загруженными символами). - person Yippie-Ki-Yay; 09.05.2012
comment
Также следует отметить, что я знаю как минимум два способа решить эту проблему, однако мой вопрос в основном касался надежности подхода «все в одном». В любом случае, спасибо за ваши усилия. - person Yippie-Ki-Yay; 09.05.2012
comment
Я страдал от мерцания экземпляров ListView даже при обновлении набора из трех элементов, которые не менялись раз в секунду. Я безрезультатно использовал BeginUpdate и EndUpdate, но это в сочетании с этим расширенным классом мгновенно решило проблему. Большое спасибо! - person LordWilmore; 18.08.2016
comment
Это расстроило дизайнера Visual Studio, потому что он не смог найти измененный класс. Я обнаружил, что получение свойства через System.Reflection и его прямая установка сработали. Я добавил кнопку для переключения между отрисовкой владельца и отрисовкой системы, чтобы сравнить их, что стало немного странно с множественным выбором, так что, возможно, лучше просто установить ее во время инициализации и оставить в покое. - person fadden; 10.05.2018

Как этот ответ здесь, хотя не уверен, но,

Думаю, что ListView.BeginUpdate() и ListView.EndUpdate() решат проблему.

Тема MSDN об этом

Может быть таким образом:

protected override void OnDrawItem(DrawListViewItemEventArgs eventArgs)
{
    ListView.BeginUpdate();
    //Do Works Here
    ListView.EndUpdate();
}

Обновить

Другой альтернативой может быть использование нового потока в BackgroundWorker для обновления ListView ... Я реализовал это вместе с _5 _ / _ 6_ в своем приложении и обнаружил, что он относительно быстрее, чем только _7 _ / _ 8_ ..

Обновить

Я нашел еще одно рабочее решение в SO, вспомогательный класс, предоставленный 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);
    }
}
person Writwick    schedule 13.05.2012
comment
По какой-то причине в моем приложении это увеличивает использование CPU до 100% (для текущего ядра). - person Yippie-Ki-Yay; 16.05.2012
comment
Ваш код постоянно пытается перерисовать ListView? - person Writwick; 16.05.2012
comment
примерно 6 лет спустя Listview все еще мерцает, а BeginUpdate и EndUpdate все еще не действуют. однако предложенный здесь EnableDoubleBuffer работает. Звонил в своем formLoad и DisableDoubleBuffer не надо. Также, чтобы уменьшить размер кода, вам не нужно перечисление ListViewExtendedStyles, просто получите значение DoubleBuffer = 0x00010000. - person AaA; 03.08.2018