Добавить элемент в 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);
});
Другие вопросы по тегам