我有一个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}调用
答案 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