Winform treeview排序属性很慢

时间:2012-08-16 11:50:47

标签: winforms treeview

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

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

啊哈,我听说你说我的IComparer对象有问题。不是根据剖析器。几乎没有时间花在排序对象上,但AddRange函数正好位于慢函数列表的顶部。

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

要说明如果我在测试项目中禁用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();
    }

将现有节点复制到数组,附加新节点,对数组进行排序然后替换尝试在树视图中内联操作节点的速度更快 - 上面代码中最慢的操作是{{1}调用

3 个答案:

答案 0 :(得分: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 在操作项目时使用的绘制过程,我们会通过删除项目来解决这些问题然后添加它们,因为它需要重新绘制以添加您要添加​​的新项目(对于每个项目)。

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

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

答案 1 :(得分:0)

我使用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();

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

答案 2 :(得分:0)

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

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