Я добавляю несколько тысяч (например, 53 709) элементов в WinForms ListView.
Попытка 1 :13,870 ms
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
Это работает очень плохо. Первое очевидное решение - позвонить BeginUpdate/EndUpdate
.
Попытка 2 :3,106 ms
listView.BeginUpdate();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
listView.EndUpdate();
Это лучше, но все же на порядок медленнее. Давайте отделим создание ListViewItems от добавления ListViewItems, чтобы найти фактического виновника:
Попытка 3 :2,631 ms
var items = new List<ListViewItem>();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
items.Add(item);
}
stopwatch.Start();
listView.BeginUpdate();
foreach (ListViewItem item in items)
listView.Items.Add(item));
listView.EndUpdate();
stopwatch.Stop()
Настоящее узкое место - это добавление предметов. Давайте попробуем преобразовать его , AddRange
а неforeach
Попытка 4: 2,182 ms
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
Чуть лучше. Будем уверены, что узкое место не вToArray()
Попытка 5: 2,132 ms
ListViewItem[] arr = items.ToArray();
stopwatch.Start();
listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();
stopwatch.Stop();
Ограничение похоже на добавление элементов в список. Может быть, другая перегрузка AddRange
, где мы добавляем, ListView.ListViewItemCollection
а не массив
Попытка 6: 2,141 ms
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
Что ж, не лучше.
Пришло время растянуть:
Шаг 1 - убедитесь, что ни один столбец не настроен на автоматическую ширину :
Проверьте
Шаг 2 - убедитесь, что ListView не пытается сортировать элементы каждый раз, когда я их добавляю:
Проверьте
Шаг 3 - спросите stackoverflow:
Проверьте
Примечание. Очевидно, что этот ListView не находится в виртуальном режиме; поскольку вы не / не можете «добавлять» элементы в представление виртуального списка (вы устанавливаете VirtualListSize
). К счастью, мой вопрос не о представлении списка в виртуальном режиме.
Есть ли что-то, чего мне не хватает, что могло бы объяснить медленное добавление элементов в список?
Бонусный чат
Я знаю, что класс Windows ListView может работать лучше, потому что я могу написать код, который делает это в 394 ms
:
ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
ListView1.Items.Add();
ListView1.Items.EndUpdate;
что по сравнению с эквивалентным кодом C # 1,349 ms
:
listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
listView.Items.Add(new ListViewItem());
listView.EndUpdate();
на порядок быстрее.
Какое свойство оболочки WinForms ListView мне не хватает?
источник
Ответы:
Я взглянул на исходный код представления списка и заметил несколько вещей, которые могут снизить производительность примерно в 4 раза, как вы видите:
в ListView.cs
ListViewItemsCollection.AddRange
вызывает вызовыListViewNativeItemCollection.AddRange
, с которых я начал свой аудитListViewNativeItemCollection.AddRange
(из строки: 18120) имеет два прохода через всю коллекцию значений, один для сбора всех отмеченных элементов, другой для их «восстановления» после вызоваInsertItems
(они оба защищены проверкойowner.IsHandleCreated
, владельцем являетсяListView
), затем вызываетBeginUpdate
.ListView.InsertItems
(из строки: 12952), первый вызов, имеет еще один обход всего списка, затем вызывается ArrayList.AddRange (возможно, еще один проход), а затем еще один проход после этого. Ведущий кListView.InsertItems
(из строки: 12952), второй вызов (черезEndUpdate
) еще один проход, через который они добавляются в aHashTable
, и aDebug.Assert(!listItemsTable.ContainsKey(ItemId))
будет еще больше замедлять его в режиме отладки. Если дескриптор не создан, он добавляет элементы вArrayList
,listItemsArray
ноif (IsHandleCreated)
затем вызываетListView.InsertItemsNative
(из строки: 3848) финальный проход по списку, где он фактически добавляется в собственный список. aDebug.Assert(this.Items.Contains(li)
дополнительно снизит производительность в режиме отладки.Таким образом, есть МНОГО дополнительных проходов по всему списку элементов в элементе управления .net, прежде чем он когда-либо сможет фактически вставить элементы в собственный список. Некоторые проходы защищены проверками создаваемого дескриптора, поэтому, если вы можете добавлять элементы до создания дескриптора, это может сэкономить вам некоторое время.
OnHandleCreated
Метод принимаетlistItemsArray
и вызовыInsertItemsNative
непосредственно без всякой дополнительной суеты.Вы можете сами прочитать
ListView
код в справочнике и посмотреть, может я что-то пропустил.В мартовском номере журнала MSDN за 2006 г. была статья под названием
Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps
.В этой статье, помимо прочего, содержались советы по повышению производительности ListViews. Похоже, это указывает на то, что быстрее добавлять элементы до создания дескриптора, но вы заплатите цену, когда элемент управления будет отрисован. Возможно, применение оптимизации рендеринга, упомянутой в комментариях, и добавление элементов до создания дескриптора позволит получить лучшее из обоих миров.
Изменить: проверяли эту гипотезу различными способами, и хотя добавление элементов перед созданием ручки происходит очень быстро, она экспоненциально медленнее, когда идет создание ручки. Я попытался обмануть его, чтобы создать дескриптор, а затем каким-то образом заставить его вызвать InsertItemsNative без дополнительных проходов, но, увы, мне помешали. Единственное, что, как я мог подумать, может быть возможным, - это создать свой Win32 ListView в проекте c ++, наполнить его элементами и использовать привязку для захвата сообщения CreateWindow, отправленного ListView при создании его дескриптора, и передачи ссылки на win32. ListView вместо нового окна ... но кто знает, на что повлияет эта сторона ... гуру Win32 должен будет рассказать об этой безумной идее :)
источник
Я использовал этот код:
ResultsListView.BeginUpdate(); ResultsListView.ListViewItemSorter = null; ResultsListView.Items.Clear(); //here we add items to listview //adding item sorter back ResultsListView.ListViewItemSorter = lvwColumnSorter; ResultsListView.Sort(); ResultsListView.EndUpdate();
Я также установил
GenerateMember
значение false для каждого столбца.Ссылка на настраиваемый сортировщик просмотра списка: http://www.codeproject.com/Articles/5332/ListView-Column-Sorter
источник
У меня та же проблема. Потом я обнаружил, что
sorter
это так медленно. Сделать сортировщик нулевымthis.listViewAbnormalList.ListViewItemSorter = null;
затем, когда щелкните сортировщик по
ListView_ColumnClick
методу, сделайте егоlv.ListViewItemSorter = new ListViewColumnSorter()
Наконец, после сортировки
sorter
снова сделайте ноль((System.Windows.Forms.ListView)sender).Sort(); lv.ListViewItemSorter = null;
источник
Поле ListView Добавить
Это простой код, который я смог создать для добавления элементов в список, состоящий из столбцов. Первый столбец - это товар, а второй - цена. В приведенном ниже коде печатается Item Cinnamon в первом столбце и 0,50 во втором столбце.
// How to add ItemName and Item Price listItems.Items.Add("Cinnamon").SubItems.Add("0.50");
Нет необходимости в создании экземпляра.
источник
Создайте все свои ListViewItems СНАЧАЛА , затем добавьте их в ListView все сразу.
Например:
var theListView = new ListView(); var items = new ListViewItem[ 53709 ]; for ( int i = 0 ; i < items.Length; ++i ) { items[ i ] = new ListViewItem( i.ToString() ); } theListView.Items.AddRange( items );
источник