Как я могу улучшить производительность проверки всех элементов в WPF TreeView? - PullRequest
3 голосов
/ 09 апреля 2011

У меня есть WPF TreeView, где у каждого TreeViewItem есть флажок.Моя реализация основана на примере, представленном в превосходной статье Джоша Смита: http://www.codeproject.com/KB/WPF/TreeViewWithCheckBoxes.aspx. Как и в этой статье, я использую следующее поведение: когда элемент отмечен или не отмечен, все его дочерние элементы должны быть проверены илиснят, соответственно.

Проблема, с которой я столкнулся, состоит в том, что мой TreeView может содержать десятки тысяч элементов.Итак, если все элементы изначально не отмечены, а я затем проверяю корневой элемент, может пройти 10-20 секунд, прежде чем все потомки будут обновлены.Это слишком медленно для моих пользователей, и мне интересно, есть ли способ ускорить обновление пользовательского интерфейса.Я вполне уверен, что здесь виноваты события PropertyChanged, поскольку, когда я их удаляю, проверка корневого элемента происходит практически мгновенно, хотя дочерние флажки не обновляются.Есть ли лучший способ обрабатывать обновление нескольких элементов интерфейса одновременно?Обратите внимание, что я пытался использовать переработку контейнеров, но это, похоже, не помогло.

Если вы хотите попробовать воспроизвести это самостоятельно, вы можете скачать проект в вышеупомянутой статье, а затем в FooViewModel.cs изменитьФункция CreateFoos () для использования этого кода:

var root = new FooViewModel("Weapons");
root.IsInitiallySelected = true;

for (int i = 0; i < 400; i++)
{
    var table = new FooViewModel("Table " + i);
    for (int j = 65; j < 91; j++)
    {
        var charItem = new FooViewModel(Convert.ToChar(j).ToString());
        for (int k = 0; k < 3; k++)
        {
            var deepItem = new FooViewModel("Item " + k);
            charItem.Children.Add(deepItem);
        }

        table.Children.Add(charItem);
    }

    root.Children.Add(table);
}

root.Initialize();
return new List<FooViewModel> { root };

Спасибо за любые идеи,

-Craig

Ответы [ 2 ]

3 голосов
/ 09 апреля 2011

Пользовательский интерфейс, представляющий древовидное представление, содержащее 10 000 элементов, вероятно, является интерфейсом, который необходимо изменить. (Хотя это не обязательно - диалоговое окно просмотра папок, по сути, делает это, если ваша файловая система достаточно волосатая.)

Две вещи, которые вы можете попробовать, при условии, что вы не пробуете виртуализацию:

  1. Привязать TreeViewItem.IsExpanded к логическому свойству в вашей модели представления узла. Узел должен вызывать события PropertyChanged, только если свойство его родительского узла IsExpanded имеет значение true. Это сохранит листовые узлы, которые не видны, от возникновения событий, которые визуальное дерево должно игнорировать. (Вы также можете обнаружить, что узел должен повышать PropertyChanged для его свойства IsChecked всякий раз, когда свойство его родителя IsExpanded становится истинным.) Это все равно будет иметь проблемы, когда дерево полностью развернуто, но если вы представьте дерево пользователю в этом состоянии с самого начала, пользователю придется работать над переводом элемента управления в состояние, в котором он начинает испытывать проблемы с производительностью.

  2. Никогда не вызывать PropertyChanged событий, когда родительский узел обновляет своих потомков. Вместо этого пусть модель представления-владельца поднимает PropertyChanged на свойстве, с которым связан ItemsSource из TreeView. Это заставит весь элемент управления повторно выполнить рендеринг, но вполне вероятно, что это будет быстрее, чем обработка отдельных обновлений со всех узлов. (Конечно, вам нужно реализовать какой-то способ, чтобы узел уведомлял своего родителя о том, что он только что обновил своих потомков - например, вызывает событие - и какой-то способ сообщить узлу, что он не должен вызывать это событие, если он обновляет свое потомки, потому что это сказал его родитель.)

1 голос
/ 09 апреля 2011

К сожалению, WPF почти всегда сталкивается с проблемами производительности при работе с таким сложным визуальным деревом, как кажется.Кроме того, если ваша viewmodel реализует только INotifyPropertyChanged (а не наследует от DependencyObject), то обновление привязок 10000+ также обычно будет медленным.Виртуализированный (что я сомневаюсь, что это касается вашей проблемы с производительностью.)

Говоря о виртуализации, это, вероятно, лучший способ решения этой проблемы.К сожалению, это, вероятно, означает написание собственной VirtualizingPanel.Вы можете начать, следуя цепочке здесь .

Если вы не хотите делать этот шаг (что понятно), то вам следует подумать о способах разгрузить некоторые изданные из дерева, возможно, для некоторых вспомогательных элементов управления, так что вы можете ограничить количество элементов, обновляемых одним щелчком мыши.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...