Winforms Winform treeview排序属性速度较慢
我有一个设置为true的winformsWinforms Winform treeview排序属性速度较慢,winforms,treeview,Winforms,Treeview,我有一个设置为true的winformsTreeView控件。我还通过将IComparer的和实例分配给TreeViewNodeSorter属性来覆盖默认的分类器 不幸的是,使用AddRange函数添加几千个节点可能需要10秒钟。如果我将Sorted设置为false,则AddRange函数将
TreeView
控件。我还通过将IComparer
的和实例分配给TreeViewNodeSorter
属性来覆盖默认的分类器
不幸的是,使用AddRange
函数添加几千个节点可能需要10秒钟。如果我将Sorted
设置为false,则AddRange
函数将<1/2秒。(请不要讨论添加这么多节点的有效性)
啊哈,我听到你说。。我的IComparer
对象中存在问题。根据档案员的说法不是这样的。排序对象几乎没有花费任何时间,而AddRange
函数正好位于慢函数列表的顶部
这个问题很容易在测试项目中复制。只需创建一个TreeNode
s列表,并使用AddRange
函数将其添加到现有的扩展树节点中。这将使用树文本的默认排序-同样,它的速度非常慢
为了演示在将节点添加到树之前,如果在测试probject中禁用Sorted
属性并在节点列表上使用List.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();
}
public static void AddSortedRange(此TreeNodeDeCollection现有节点、IList节点、TreeView TreeView、IComparer分拣机)
{
TreeNode[]数组=新的TreeNode[nodes.Count+existingNodes.Count];
existingNodes.CopyTo(数组,0);
CopyTo(数组,existingNodes.Count);
数组.排序(数组,分类器);
treeView.BeginUpdate();
existingNodes.Clear();
existingNodes.AddRange(数组);
EndUpdate();
}
将现有节点复制到数组、附加新节点、对数组进行排序,然后替换试图在树状视图中内联操作节点的数组会更快—上述代码中最慢的操作是
existingNodes.Clear()
call您遇到的性能问题与您正在将项目添加到已排序的树视图中有关。当你添加到一个排序列表中时,幕后发生的事情是,对于你正在添加的每个项目,它试图找到它的位置,这意味着它需要遍历整个列表每个项目,现在想象一下每个新项目的迭代次数:)
你能做的是:
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方法来禁用树状视图在操作其项目时使用的绘制过程,这将通过删除项目然后再添加项目来实现,因为需要重新绘制以添加您正在添加的新项目(针对每个项目)
在对节点集合进行任何更改之前我们必须调用Sorted=false;以禁用排序(这只是暂时的-由于SuspendLayout,用户将看不到任何更改)。
然后只需将项目添加到集合中(因为树视图目前没有排序,所以应该非常快)。
然后我们通过调用Sorted=true来再次启用排序;将Sorted属性设置为true将导致集合进行排序。
这样,排序只执行一次(因此,TreeView只对项目执行一次)
还有一件事,如果您为ListView(tv.ListViewItemSorter)定义了一个自定义排序器,请在添加项目之前将其设置为null,当然这只是暂时的,在ResumeLayout调用之前再次启用它。我使用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();
}
我几乎不明白这里发生了什么。为什么它在过去起作用,突然停止了。坦白说,我没有足够的时间进一步挖掘,无论如何,最重要的是在这里:它再次起作用。我对TreeView控件做了一个简单的扩展。它非常快。它将内部存储移动到一个字典,这将产生巨大的差异。在y现实世界的例子我有100000条记录需要加载。之前需要37分钟,但现在需要2.2秒
您可以在CodeProject上找到示例和代码:添加前排序如何?难道您不能仅在添加所有节点后将Sorted设置为true吗?@UweKeim-如问题中所述,在现有节点集中插入节点需要做大量额外的工作,尤其是将一个范围合并到现有节点中collection@ken2k-那太棒了将延迟移到Sorted属性..,这与将Sorted保留为false并在Add之后调用Sort()函数相同
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();