C#中的慢树视图
我有一个用Cand编写的遗留应用程序,它显示了一个非常复杂的树视图,其中包含10到2万个元素。
过去,我遇到了类似的问题(但在C ++中),我使用Win32 API提供的OWNERDATA功能解决了该问题。
C#中有类似的机制吗?
编辑:计划是优化创建时间以及浏览时间。在这两种情况下,均可通过Win32 API使用的方法非常出色,因为它可以将初始化时间减少为零,并且对元素的请求数仅限于在任何一次可见的请求。
Joshl:我们实际上已经在按照建议做,但是我们仍然需要更高的效率。
解决方案
一种提高性能的技术是在用户扩展树视图时加载TreeNodes。通常,用户将不需要一次在其屏幕上打开20,000个节点。仅加载用户需要查看的级别,以及正确显示给用户的能力所需的任何子级信息(如果存在子级,则展开图标,计数,图标等)。随着用户扩展节点,及时加载子级。
来自Keith的有用提示:使用winforms TreeView,我们需要至少有一个子节点,否则它将不会显示扩展[+],但是我们需要处理TreeNodeExpanded事件以删除该虚拟节点并填充子节点。
我不相信.NET TreeView支持我们想要的东西,尽管.NET的DataGridView支持这种类型的模型(请参阅DataGridView的VirtualMode属性)。 TreeView将允许我们绘制自己的节点,但不允许我们从某些虚拟存储中填充它们。
如果可能,我们可能要考虑为应用程序使用DataGridView。否则,如果我们可以解决在扩展节点时正确刷新屏幕的一些问题,则可以手动管理节点(如上面的joshl所述)。除此之外,我们可能想检查一些第三方供应商,例如该第三方供应商(Divelements SandGrid),它们可能(强调)可能支持我们所需的操作模式。
注意:自2013年7月底起,Develements不支持SandGrid。
有一种方法可以使TreeView的性能更好,即创建所有子节点并将其挂钩在一起,然后将节点添加到TreeView中。如果是图形性能,我们正在谈论。
TreeView tree = new TreeView(); TreeNode root = new TreeNode("Root"); PopulateRootNode(root); // Get all your data tree.Nodes.Add(root);
否则,使用OnTreeNodeExpanded逐节点加载它们。
注意:提问者说他已经做过这种事情,因此该答案被无效,但是我决定仍然将其发布,以供将来对此主题进行搜索的其他参考
过去做过类似的事情时,我倾向于选择朴素的延迟加载样式。
- 使用TreeNode.Tag属性保存一个引用,我们可以使用该引用来查找子级
- 使用TreeView.BeforeExpand事件填充子节点
- (可选)使用TreeView.AfterCollapse事件将其删除。
- 为了使[+] / [-]框出现,我发现的最佳方法是创建一个单例虚拟树节点,将其作为子节点添加到所有未填充的节点中,并在填充之前检查其是否存在
BeforeExpand
。
对于Windows C编程中的大数据(无论是WPF还是WinForms),传统上我是动态添加节点的。我加载初始树根+子级+孙子级。扩展任何节点后,我将加载表示扩展节点的孙子节点的树节点(如果有)。
这种模式也适用于数据检索。如果我们确实要从成千上万条记录的源中加载数据,则可能不希望全部加载。没有用户愿意等待加载,也没有理由加载可能永远不会被查看的数据。
通常,我通常根据需要在后台线程上加载了孙子节点或者曾孙子节点数据,然后将这些数据编组回UI线程并创建并添加节点。这使UI保持响应状态。我们可以在视觉上装饰树节点,以指示在用户超前IO到数据存储的情况下它们仍在加载。
在我们的主要WinForm应用程序中,我们将一幅treeview加载到了一个镜头中:
- BeginUpdate()
- 加载20.000个节点
- EndUpdate()
到目前为止,性能仍然不错。实际上,它是我们不替换为第三方组件的少数组件之一。
以我的经验,在不调用Begin / EndUpdate()的情况下加载节点(一次拍摄或者按需)时,TreeView的性能会变慢,特别是如果节点已排序,但是如果正确调用Begin / EndUpdate(),则不应真正遇到与组件本身有关的性能问题。