我有一个WPF TreeView,其中每个TreeViewItem都有一个复选框。我的实现基于Josh Smith的优秀文章http://www.codeproject.com/KB/WPF/TreeViewWithCheckBoxes.aspx中提供的示例。正如文章中所述,我正在使用的行为是,当选中或取消选中某个项时,应分别检查或取消选中其所有子项。
我遇到的问题是我的TreeView可以包含数万个项目。因此,如果所有项目最初都未选中,然后我检查根项目,则可能需要10-20秒才能更新所有后代。这对我的用户来说太慢了,我想知道是否有办法加快UI更新。我很确定PropertyChanged事件是这里的罪魁祸首,因为当我删除它们时,检查根项几乎是即时的,尽管子复选框没有得到更新。有没有更好的方法来同时处理多个UI元素?请注意,我尝试使用容器回收,但这似乎没有帮助。
如果您想尝试自己复制,可以在上述文章中下载项目,然后在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
答案 0 :(得分:3)
呈现包含10,000个项目的树视图的UI可能是需要重新设计的UI。 (虽然不一定 - 如果您的文件系统足够毛茸茸,那么浏览文件夹对话框基本上会这样做。)
假设您不尝试虚拟化,可以尝试两件事:
将TreeViewItem.IsExpanded
绑定到节点视图模型中的布尔属性。如果节点的父节点的PropertyChanged
属性为true,则该节点应仅引发IsExpanded
个事件。这将保持不可见的叶节点引发可视树必须忽略的事件。 (您可能还会发现,只要父级的PropertyChanged
属性变为true,您就需要在IsChecked
属性上引发节点IsExpanded
。)当树是完全展开,但除非您在开始时将树呈现给处于此状态的用户,否则用户必须努力将控件置于其开始遇到性能问题的状态。
当父节点更新其后代时,不要引发PropertyChanged
事件。相反,让拥有PropertyChanged
的{{1}}所绑定的属性的拥有视图模型引发ItemsSource
。这将强制整个控件重新渲染,但这很可能比处理来自所有节点的单个更新更快。 (当然,你需要为节点实现某种方式来通知它的父节点它刚刚更新了它的后代 - 例如引发一个事件 - 以及某种方式告诉节点如果它正在更新它的话它不应该引发它后代,因为它的父母告诉它。)
答案 1 :(得分:1)
不幸的是,在处理像您似乎拥有的复杂的可视化树时,WPF几乎总是遇到性能问题。此外,如果您的viewmodel仅实现INotifyPropertyChanged(而不是继承自DependencyObject),那么更新10000+绑定通常也会很慢。
作为参考,如果您的ItemsPanel已虚拟化(我怀疑它正在考虑您的性能问题),容器回收只会有用。
说到虚拟化,这可能是解决此问题的最佳方法。不幸的是,这可能意味着编写自定义VirtualizingPanel。您可以按照链here开始。
如果您不想采取这一步骤(这是可以理解的),那么您应该考虑从树中卸载一些数据的方法,也许是一些辅助控制,这样您就可以限制多少项目。只需单击即可更新。