C#中的慢树视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/121112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 14:32:03  来源:igfitidea点击:

Slow treeview in C#

提问by Fabien Hure

I have a legacy application that is written in C# and it displays a very complex treeview with 10 to 20 thousand elements.

我有一个用 C# 编写的遗留应用程序,它显示一个包含 10 到 20 000 个元素的非常复杂的树视图。

In the past I encountered a similar problem (but in C++) that i solved with the OWNERDATA capability offered by the Win32 API.

过去,我遇到了类似的问题(但在 C++ 中),我使用 Win32 API 提供的 OWNERDATA 功能解决了该问题。

Is there a similar mechanism in C#?

C# 中是否有类似的机制?

EDIT: The plan is to optimize the creation time as well as browsing time. The method available through Win32 API is excellent in both of these cases as it reduce initialization time to nothing and the number of requests for elements are limited to only the ones visible at any one time. Joshl: We are actually doing exactly what you suggest already, but we still need more efficiency.

编辑:计划是优化创建时间和浏览时间。通过 Win32 API 可用的方法在这两种情况下都非常出色,因为它将初始化时间减少到零,并且对元素的请求数量仅限于任何一次可见的元素。Joshl:我们实际上已经按照你的建议做了,但我们仍然需要更高的效率。

采纳答案by Ken Wootton

I don't believe the .NET TreeView supports what you want, although this type of model is supported by .NET's DataGridView (see DataGridView's VirtualModeproperty). The TreeView will let you draw your own nodes but it won't let you populate them from some virtual store.

我不相信 .NET TreeView 支持您想要的,尽管 .NET 的 DataGridView 支持这种类型的模型(请参阅 DataGridView 的VirtualMode属性)。TreeView 将让您绘制自己的节点,但它不会让您从某个虚拟商店填充它们。

If possible, you might want to consider the use of a DataGridView for your application. If not, managing the nodes manually (like joshl mentions above) might work if you can get around some issues with refreshing the screen properly when nodes are expanded. Outside of that, you might want to check out some of the third party vendors, like this one (Divelements SandGrid), that might (emphasis on might) support your desired mode of operation.

如果可能,您可能需要考虑为您的应用程序使用 DataGridView。如果没有,手动管理节点(如上面提到的 joshl)可能会起作用,如果您可以解决在节点展开时正确刷新屏幕的一些问题。除此之外,您可能想查看一些第三方供应商,比如这个 (Divelements SandGrid),它们可能(强调可能)支持您想要的操作模式。

NOTE: The SandGrid is not supported by Divelements as of the end of July 2013.

注意:截至 2013 年 7 月末,Divelements 不支持 SandGrid。

回答by JoshL

One technique for improving performance is to load TreeNodes as the user expands the treeview. Normally a user will not require 20,000 nodes to be open on their screen at once. Only load the level that the user needs to see, along with whatever child information you need to properly display affordances to the user (expand icon if children exist, counts, icons, etc). As the user expands nodes, load children just in time.

提高性能的一种技术是在用户展开树视图时加载 TreeNode。通常,用户不需要一次在其屏幕上打开 20,000 个节点。仅加载用户需要查看的级别,以及您需要向用户正确显示可供性的任何子信息(如果存在儿童,则展开图标、计数、图标等)。随着用户扩展节点,及时加载子节点。

Helpful hint from Keith: With the winforms TreeView you need to have at least one child node or it won't show the expand [+], but then you handle the TreeNodeExpanded event to remove that dummy node and populate the children.

来自 Keith 的有用提示:使用 winforms TreeView,您需要至少有一个子节点,否则它不会显示展开 [+],但随后您处理 TreeNodeExpanded 事件以删除该虚拟节点并填充子节点。

回答by Seb Nilsson

There is one way to make the TreeView perform much better and that is to create all sub-nodes and hook them together and thenadd the nodes to the TreeView. If it's the graphical performance we are talking about.

有一种方法可以使 TreeView 表现得更好,那就是创建所有子节点并将它们连接在一起,然后将节点添加到 TreeView。如果是我们谈论的图形性能。

TreeView tree = new TreeView();
TreeNode root = new TreeNode("Root");
PopulateRootNode(root); // Get all your data
tree.Nodes.Add(root);

Otherwise, load them node by node using OnTreeNodeExpanded.

否则,使用OnTreeNodeExpanded逐个节点加载它们。

回答by Alex Lyman

NOTE: This answer is invalidated by an edit by the questioner saying he already does this kind of thing, but I decided to still post it for future reference by others searching on this topic

注意:这个答案因提问者的编辑而无效,说他已经做了这种事情,但我决定仍然发布它以供其他搜索此主题的人将来参考

When I've done similar things in the past, I've tended to opt for the naive lazy-loading style.

当我过去做过类似的事情时,我倾向于选择天真的延迟加载风格。

  • Use the TreeNode.Tagproperty to hold a reference that you can use to look-up the children
  • Use the TreeView.BeforeExpandevent to populate the child nodes
  • Optionally use the TreeView.AfterCollapseevent to remove them.
  • To get the [+]/[-] boxes to appear, the best way I've found is to create a singleton dummy TreeNodethat gets added as a child to all unpopulated Nodes, and you check for its existence before populating with BeforeExpand.
  • 使用该TreeNode.Tag属性来保存可用于查找子项的引用
  • 使用TreeView.BeforeExpand事件填充子节点
  • (可选)使用TreeView.AfterCollapse事件来删除它们。
  • 为了让 [+]/[-] 框出现,我发现的最好方法是创建一个单例虚拟对象TreeNode,将其作为子节点添加到所有未填充的节点中,并在填充BeforeExpand.

回答by Jason Hymanson

For large data in Windows C# programming, whether it be in WPF or WinForms, I have traditionally added nodes dynamically. I load the initial tree root + children + grandchildren deep. When any node is expanded, I load the tree nodes that would represent the grandchildren of the expanding node, if any.

对于Windows C#编程中的大数据,无论是WPF还是WinForms,我传统上都是动态添加节点的。我加载初始树根+孩子+孙子很深。当任何节点被扩展时,我加载将代表扩展节点的孙节点的树节点(如果有)。

This pattern also works well with data retrieval. If you are truly loading data from a source of thousands or millions of records you probably don't want to load those all up front. No user wants to wait for that to be loaded, and there is not reason to load data that may never be viewed.

这种模式也适用于数据检索。如果您真的要从数千或数百万条记录的来源加载数据,您可能不想预先加载这些数据。没有用户愿意等待加载,并且没有理由加载可能永远不会被查看的数据。

I have typically loaded the grandchildren or the great-grandchildren node data as needed on a background thread, then marshal those data back to the UI thread and create and add the nodes. This leaves the UI responsive. You can visually decorate tree nodes to indicate they are still loading for the case where a user gets ahead of your IO to the data store.

我通常根据需要在后台线程上加载孙子节点或曾孙子节点数据,然后将这些数据编组回 UI 线程并创建和添加节点。这使 UI 具有响应性。您可以在视觉上装饰树节点,以表明它们仍在加载,以防用户提前 IO 访问数据存储。

回答by Filini

In our main WinForm app, we have a treeview loaded all in one shot:

在我们的主要 WinForm 应用程序中,我们一次性加载了一个树视图:

  • BeginUpdate()
  • Load 20.000 nodes
  • EndUpdate()
  • 开始更新()
  • 加载 20.000 个节点
  • 结束更新()

and so far the performance is still nice. It is actually one of the few components we are not replacing with third party ones.

到目前为止表现还是不错的。它实际上是我们不会用第三方组件替换的少数组件之一。

The TreeView performance, in my experience, gets slow when you load nodes (in one shot, or on demand) without calling Begin/EndUpdate(), especially if your nodes are sorted, but if you call Begin/EndUpdate() correctly, you shouldn't really get performance issues related to the component itself.

根据我的经验,当您加载节点(一次性或按需)而不调用 Begin/EndUpdate() 时,TreeView 性能会变慢,特别是如果您的节点已排序,但如果您正确调用 Begin/EndUpdate(),您不应该真正得到与组件本身相关的性能问题。