Мерцание в элементе управления ListView (OwnerDraw, Virtual)
Этот вопрос можно считать продолжением Мерцание в listview с помощью ownerdraw и virtualmode.
У меня есть элемент управления ListView
в Virtual mode
, и я пытаюсь выполнить пользовательский чертеж. Выделение позиции выполняется с помощью следующего метода переопределения:
protected override void OnDrawItem(DrawListViewItemEventArgs eventArgs)
Как упоминалось в упомянутом вопросе, пользовательский чертеж вводит мерцание мыши над событиями. Отладчик говорит мне, что это происходит из-за чрезмерного количества пользовательских событий рисования, которые увольняются.
Теперь - принятый ответ на указанный вопрос говорит нам:
Это ошибка в .NET ListView, и вы не можете обойти ее двойная буферизация.
-
Итак, насколько надежна эта информация? Неужели это ошибка? Или, может быть, мы просто пытаемся отрезать часть сообщений и надеемся, что это не изменит видимого поведения?
-
Является ли это правдой, что если у меня есть моя процедура рисования владельца для ListView
в Virtual Mode,
, я могу подавить эти события Custom Draw
и выполнить только мой рисунок в WM_PAINT
или, может быть, это неверно для некоторых случаев?
-
Каковы предпосылки для элемента управления System.Windows.Forms
, чтобы он мог выполнять всю картину в WM_PAINT
без изменения ее начального поведения?
Ответы
Ответ 1
Я видел эту мерцающую проблему с элементом управления ListView в любых обработчиках обработчиков обработчиков (DrawItem, DrawSubItem). Я попробовал BeginUpdate()/EndUpdate() и двойную буферизацию без успеха. Я думаю, что .NET запускает дополнительный WM_PAINT ко всем столбцам до справа от настраиваемого столбца.
Однако я нашел это обходное решение для отдельного персонализированного столбца ListView. Он отлично работает!
- В редакторе headheader установите столбец с пользовательским рисунком как последний столбец
- Измените "DisplayIndex" позиции, которую вы хотите.
Это должно решить мерцание при наведении указателя мыши или рендеринг времени выполнения.
Ответ 2
По крайней мере, для двойной буферизации для OnDrawItem неверно, что есть ошибка, но это немного глупо: есть защищенный атрибут, который вы можете установить, но вам нужно переопределить ListView. Я создал такой класс:
public class MyListView : ListView
{
public MyListView()
: base()
{
DoubleBuffered = true;
}
}
И затем в моем файле MyForm.Designer.cs я изменяю экземпляр ListView со следующей строкой:
private ListView myListView;
this.myListView = new MyListView();
И OnDrawItem будет работать как шарм!
Ответ 3
Как Этот ответ здесь, хотя и не уверен, но
Я думаю, что ListView.BeginUpdate()
и ListView.EndUpdate()
решают проблему.
Сообщение MSDN об этом
Возможно, на этом пути:
protected override void OnDrawItem(DrawListViewItemEventArgs eventArgs)
{
ListView.BeginUpdate();
//Do Works Here
ListView.EndUpdate();
}
Обновление
Другая альтернатива может использовать новый поток в BackgroundWorker
для обновления ListView...
Я реализовал это вместе с BeginUpdate()
/EndUpDate()
в своем приложении и нашел его относительно быстрее, чем только BeginUpdate()
/EndUpDate()
..
Обновление
Я нашел другое рабочее решение в 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 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);
}
}