Добавить элемент в ListView слишком медленно в WPF
Моя проблема в том, что мне нужно добавить много элементов в Listview в WPF. В WinForms вы просто используете BeginUpdate()
метод, добавить все и, наконец, использовать EndUpdate()
метод.
Итак, как мне остановить рисование в WPF ListView, пока не будет добавлен каждый элемент, а затем нарисовать все за один раз?
foreach (FilePaths filePath in directoryPath.GetFilePaths())
{
GetFileListViewItem(filePath);
}
В этом GetFileListViewItem
метод, я добавляю элемент в просмотр списка.
private void GetFileListViewItem(FilePaths filePath)
{
string ext = GetExtension(filePath.GetPath());
string fileName = GetFileNameWithoutExtension(filePath.GetPath());
string type = "";
if (ext != "")
{
type = ext.ToUpper().Substring(1) + " File";
}
else
{
type = "Unknown";
}
fileListView.Items.Add(new FileListItem
{
Name = fileName,
Type = type
});
}
4 ответа
У меня есть похожая проблема, и я решил ее с помощью добавления задержки, я кодирую пользовательское добавление, например, с задержкой 500 миллисекунд, если у меня есть еще какие-либо добавления в это время, поэтому я жду следующие 500 миллисекунд. это происходит, когда вы часто добавляете слишком много элементов, поэтому в вашей форме будет только один рендер.
Я думаю, что, имея дело с WPF, вам лучше избавиться от менталитета WinForms, заключающегося в непосредственном манипулировании элементами управления в коде. Одна из самых сильных сторон WPF - возможности связывания данных.
ListView
контроль (все, что наследует от ItemsControl
оказывается) реализует виртуализацию пользовательского интерфейса. По этой ссылке: WPF: виртуализация данных
Когда WPF ItemsControl привязан к большому источнику данных коллекции с включенной виртуализацией пользовательского интерфейса, элемент управления будет создавать только визуальные контейнеры для элементов, которые фактически видимы (плюс несколько над и под). Обычно это лишь небольшая часть всей коллекции. Когда пользователь выполняет прокрутку, новые визуальные контейнеры создаются, когда элементы становятся видимыми, а старые контейнеры удаляются, когда элементы больше не видны. Когда утилизация контейнера включена, она будет повторно использовать визуальные контейнеры вместо создания и удаления, избегая создания экземпляров объекта и накладных расходов на сборку мусора.
Виртуализация пользовательского интерфейса означает, что элементы управления могут быть связаны с большими коллекциями, не занимая большой объем памяти из-за визуальных контейнеров. Однако существует потенциально большой объем памяти из-за фактических объектов данных в коллекции.
Однако, исходя из ответа на этот вопрос, кажется, что виртуализация срабатывает только тогда, когда данные связывают ItemsSource
собственность на коллекцию. Таким образом, казалось бы, что непосредственное добавление элементов в ListView
Как вы делаете, это предотвращает виртуализацию.
Использовать привязку данных
Привязать источник данных ListVies к MyFileListItem
Волшебная скорость случится
Я думаю, что добавление прямых разрывов виртуализации
Он рисует один раз, но вы генерируете несколько уведомлений об изменениях пользовательского интерфейса
Это будет просто одна привязка данных
Если вы собираетесь добавить и удалить, тогда используйте ObservableCollection
Если значения изменятся в FileListItem, то вам нужно реализовать измененный iNotifyProperty
private List<FileListItem> myFileListItems;
public List<FileListItem> MyFileListItems
{
get
{
if (myFileListItems == null)
{
myFileLinstItems = new List<FileListItem>();
foreach (FilePaths filePath in directoryPath.GetFilePaths())
{
string ext = GetExtension(filePath.GetPath());
if (String.IsNullOrEmpty(ext)
ext = "Unknown";
else
ext = ext.ToUpper().Substring(1) + " File";
myFileListItems.Add(new FileListItem
{
Name = GetFileNameWithoutExtension(filePath.GetPath());,
Type = ext
});
}
}
return myFileListItems;
}
}
Попробуйте выполнить код внутри Dispatcher.BeginInvoke с приоритетом ниже DispatcherPriority.Render
Это должно добавить все элементы перед визуализацией.
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render, (Action)delegate
{
GetFileListViewItem(filePath);
});