【问题标题】:Winform treeview sorted property is slowWinform treeview 排序属性很慢
【发布时间】:2012-08-16 11:50:46
【问题描述】:

我有一个将Sorted property 设置为true 的winforms TreeView 控件。我还通过将IComparer 的实例分配给TreeViewNodeSorter 属性来覆盖默认排序器。

不幸的是,使用AddRange 函数添加几千个节点可能需要 10 秒。如果我将 Sorted 设置为 false,则 AddRange 函数小于 1/2 秒。 (请不要讨论添加这么多节点的有效性)

啊哈,我听到你说.. 我的 IComparer 对象有问题。不是根据探查器。几乎没有时间花在排序对象上,但 AddRange 函数却在慢函数列表的顶部。

这个问题很容易在测试项目中复制。只需创建一个TreeNodes 列表,然后使用AddRange 函数将其添加到现有的展开树节点中。这将使用树文本上的默认排序 - 再次慢得不成比例。

为了演示如果我在测试 probject 中禁用 Sorted 属性并在我的节点列表上使用 List<T>.Sort 函数(带有比较节点文本的委托),然后再将它们添加到树几乎没有延迟。

这导致在使用AddRange 之前手动对节点进行排序的解决方法。没关系,但是在将节点添加到现有的子节点集时找到正确的插入点意味着需要做很多工作 - 而不是简单地将 Sorted 设置为 true。

有没有办法加快这种行为?

编辑 - 似乎唯一的方法是在添加之前进行排序。这有点麻烦,但我想出了以下扩展方法:

public static void AddSortedRange(this TreeNodeCollection existingNodes, IList<TreeNode>    nodes, TreeView treeView, IComparer sorter)
    {
        TreeNode[] array = new TreeNode[nodes.Count + existingNodes.Count];

        existingNodes.CopyTo(array, 0);

        nodes.CopyTo(array, existingNodes.Count);

        Array.Sort(array, sorter);

        treeView.BeginUpdate();

        existingNodes.Clear();

        existingNodes.AddRange(array);

        treeView.EndUpdate();
    }

将现有节点复制到数组、附加新节点、对数组进行排序然后替换尝试在树视图中内联操作节点的操作更快 - 上述代码中最慢的操作是 existingNodes.Clear() 调用

【问题讨论】:

  • 在添加之前排序如何?
  • 你不能只在添加所有节点后才将 Sorted 设置为 true 吗?
  • @UweKeim - 如问题所述,在现有节点集中插入节点需要做大量额外工作 - 特别是将范围合并到现有集合中
  • @ken2k - 只是将延迟转移到 Sorted 属性.. 将 Sorted 保留为 false 并在 Add 之后调用 Sort() 函数很遗憾

标签: winforms treeview


【解决方案1】:

您遇到的性能问题与您将项目添加到已排序的 TreeView 的事实有关。当您添加到排序列表时,幕后发生的事情是,对于您添加的 每个 项目,它会尝试找到它的位置,这意味着它需要遍历 整个列表 对于每个项目,现在想象一下每个新项目需要多少次迭代:)

你可以这样做:

TreeView tv = new TreeView(); // Just so I have a TreeView variable
TreeNode[] nodes = ... // Well, your list of nodes that you want to add
tv.SuspendLayout();
tv.Sorted = false;
tv.Nodes.Clear();
tv.Nodes.AddRange( nodes );
tv.Sorted = true;
tv.ResumeLayout();

出于性能原因,我们使用 SuspendLayout/ResumeLayout 方法来禁用 TreeView 在操作其项目时使用的绘制过程,我们会通过删除项目来实现然后也添加它们,因为它需要重新绘制以添加您正在添加的新项目(对于每个项目)。

在我们对 Nodes Collection 进行任何更改之前,我们必须调用 Sorted = false;禁用排序(这只是暂时的 - 由于 SuspendLayout,用户不会看到任何更改)。 然后只需将项目添加到集合中(因为 TreeView 暂时没有排序,它应该非常快)。 然后我们通过调用 Sorted = true 再次启用排序;将 Sorted 属性设置为 true 将导致集合进行排序。 这样,排序将只执行一次(因此 TreeView 只会遍历项目一次)。

还有一件事,如果您为 ListView (tv.ListViewItemSorter) 定义了自定义排序器,请在添加项目之前将其设置为 null,当然只是暂时的,在 ResumeLayout 调用之前再次重新启用它。

【讨论】:

    【解决方案2】:

    我在使用 Sort() 方法时遇到了锁定情况。

    它运行了好几个星期,然后有一次卡住了,我的应用程序在任务管理器中卡住了 25% 的 CPU。

    var allTags = _TagEngine.GetTags(1, force);
    
    try
    {
        TagTree.BeginUpdate();
        TagTree.Nodes.Clear();
        foreach (var rec in allTags)
        {
           ... adding nodes in the tree
        }
    
        TagTree.Sort(); // <= stuck here !
    }
    finally
    {
        TagTree.EndUpdate();
    }
    

    所以我使用反编译器观察了 Sort() 方法的内部,我注意到它已经在内部处理了 BeginUpdate/EndUpdate 功能。

    然后我将 TagTree.Sort() 移到 BeginUpdate/EndUpdate 之外,它工作正常。

    var allTags = _TagEngine.GetTags(1, force);
    
    try
    {
        TagTree.BeginUpdate();
        TagTree.Nodes.Clear();
        foreach (var rec in allTags)
        {
           ... adding nodes in the tree
        }
    }
    finally
    {
        TagTree.EndUpdate();
    }
    
    TagTree.Sort();
    

    我几乎不明白这里发生了什么。为什么它在过去起作用,然后突然停止了。坦率地说,我没有足够的时间进一步挖掘,无论如何,最重要的是在这里:它又可以工作了。

    【讨论】:

      【解决方案3】:

      我对 TreeView 控件做了一个简单的扩展。它非常快。它将内部存储移动到字典中,这会产生巨大的影响。在我的真实示例中,我需要加载 100000 条记录。之前需要 37 分钟,现在需要 2.2 秒!!

      您可以在 CodeProject 上找到示例和代码:http://www.codeproject.com/Articles/679563/Fast-TreeView

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-09
        • 2021-12-28
        • 2010-11-15
        • 1970-01-01
        • 2010-12-10
        相关资源
        最近更新 更多