Каков правильный / управляемый путь к SelectAll
в списке .NET в виртуальном режиме ?
Когда в ListView включен VirtualMode
, понятие выбора ListViewItem
исчезает. Единственное, что вы выбираете - это индексы . Они доступны через свойство SelectedIndices
.
Обходной путь # 1
Первый способ - добавить итеративно добавить каждый индекс в коллекцию SelectedIncides
:
this.BeginUpdate();
try
{
for (int i = 0; i < this.VirtualListSize; i++)
this.SelectedIndices.Add(i);
}
finally
{
this.EndUpdate();
}
Помимо того, что он плохо спроектирован (занятый цикл насчитывает до ста тысяч), он плохо работает ( он генерирует событие OnSelectedIndexChanged
каждую итерацию ). Учитывая, что просмотр списка находится в виртуальном режиме, вполне разумно ожидать, что в списке будет немало элементов.
Обходной путь # 2
Элемент управления Windows ListView
полностью способен выбирать все элементы одновременно. Отправка списка * сообщением LVM_SETITEMSTATE
, сообщив об этом так, чтобы «выбрать» все элементы .:
LVITEM lvi;
lvi.stateMask = 2; //only bit 2 (LVIS_SELECTED) is valid
lvi.state = 2; //setting bit two on (i.e. selected)
SendMessage(listview.Handle, LVM_SETITEMSTATE, -1, lvi); //-1 = apply to all items
Это работает достаточно хорошо. Это происходит мгновенно, и самое большее только два события происходят:
class NativeMethods
{
private const int LVM_SETITEMSTATE = LVM_FIRST + 43;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct LVITEM
{
public int mask;
public int iItem;
public int iSubItem;
public int state;
public int stateMask;
[MarshalAs(UnmanagedType.LPTStr)]public string pszText;
public int cchTextMax;
public int iImage;
public IntPtr lParam;
public int iIndent;
public int iGroupId;
public int cColumns;
public IntPtr puColumns;
};
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageLVItem(HandleRef hWnd, int msg, int wParam, ref LVITEM lvi);
/// <summary>
/// Select all rows on the given listview
/// </summary>
/// <param name="listView">The listview whose items are to be selected</param>
public static void SelectAllItems(ListView listView)
{
NativeMethods.SetItemState(listView, -1, 2, 2);
}
/// <summary>
/// Set the item state on the given item
/// </summary>
/// <param name="list">The listview whose item's state is to be changed</param>
/// <param name="itemIndex">The index of the item to be changed</param>
/// <param name="mask">Which bits of the value are to be set?</param>
/// <param name="value">The value to be set</param>
public static void SetItemState(ListView listView, int itemIndex, int mask, int value)
{
LVITEM lvItem = new LVITEM();
lvItem.stateMask = mask;
lvItem.state = value;
SendMessageLVItem(new HandleRef(listView, listView.Handle), LVM_SETITEMSTATE, itemIndex, ref lvItem);
}
}
Но это зависит от взаимодействия P / Invoke. Это также основывается на том факте, что .NET ListView
является оболочкой для элемента управления Windows ListView
. Это не всегда так.
Так что я надеюсь, что правильный, управляемый способ SelectAll
- это .NET WinForms ListView.
Бонусная болтовня
Нет необходимости прибегать к P / Invoke для отмены выбора всех элементов в списке:
LVITEM lvi;
lvi.stateMask = 2; //only bit 2 (LVIS_SELECTED) is valid
lvi.state = 1; //setting bit two off (i.e. unselected)
SendMessage(listview.Handle, LVM_SETITEMSTATE, -1, lvi); //-1 = apply to all items
управляемый эквивалент такой же быстрый:
listView.SelectedIndices.Clear();
Чтение бонусов