How can I improve the performance of checking all items in a WPF TreeView?
I have a WPF TreeView where each TreeViewItem has a checkbox. My implementation is based on the sample provided in Josh Smith's excellent article: http://www.codeproject.com/KB/WPF/TreeViewWithCheckBoxes.aspx. As in the article, the behavior I'm using is that when an item is checked or unchecked, all of its child items should be checked or unchecked, respectively.
The problem I have is that my TreeView can contain tens of thousands of items. So, if all the items are initially unchecked and I then check the root item, it may take 10-20 seconds before all the descendants are updated. This is too slow for my users and I'm wondering if there's a way to speed up the UI update. I'm fairly certain that the PropertyChanged events are the culprit here, since when I remove them, checking the root item is nearly instant, although the child checkboxes don't 开发者_运维问答get updated. Is there a better way to handle updating multiple UI elements simultaneously? Note that I tried using container recycling but that didn't seem to help.
If you want to try reproducing this yourself, you can download the project in the aforemented article, and then in FooViewModel.cs, change the CreateFoos() function to use this code:
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 };
Thanks for any ideas,
-Craig
A UI presenting a tree view that contains 10,000 items is probably a UI that needs to be redesigned. (Though not necessarily - the browse-for-a-folder dialog essentially does this if your file system is hairy enough.)
Two things that you can try, assuming that you don't try virtualization:
Bind
TreeViewItem.IsExpanded
to a boolean property in your node view model. A node should only raisePropertyChanged
events if its parent node'sIsExpanded
property is true. This will keep leaf nodes that aren't visible from raising events that the visual tree has to ignore. (You may also find that you'll need to have a node raisePropertyChanged
on itsIsChecked
property whenever its parent'sIsExpanded
property becomes true.) This will still have problems when the tree is fully expanded, but unless you present the tree to the user in this state at the outset, the user will have to work at getting the control into a state where it starts experiencing performance issues.Don't ever raise
PropertyChanged
events when a parent node updates its descendants. Instead, have the owning view model raisePropertyChanged
on the property that theItemsSource
of theTreeView
is bound to. This will force the whole control to re-render, but it's very likely that this will be faster than handling individual updates from all of the nodes. (Of course, you'll need to implement some way for a node to notify its parent that it just updated its descendants - e.g. raising an event - and some way to tell a node that it shouldn't raise this event if it's updating its descendants because its parent told it to.)
Unfortunately, WPF almost always runs into performance issues when dealing with as complex a visual tree as you seem to have. Also, if your viewmodel is only implementing INotifyPropertyChanged (rather than inheriting from DependencyObject) then updating 10000+ bindings is also generally going to be slow.
For reference, container recycling is only going to be useful if your ItemsPanel is virtualized (which I doubt it is considering your performance issue.)
Speaking of virtualization, that is probably the best way to address this issue. Unforunately, that likely means writing a custom VirtualizingPanel. You can get a start by following the chain here.
If you don't want to take that step (which is understandable,) then you should think about ways to offload some of the data from the tree, perhaps to some auxillary controls, so that you can limit how many items are updated with a single click.
精彩评论