如何提高填充大量树视图的性能?

Jer*_*dge 7 delphi treeview performance large-data

首先,我正在回答我自己的问题Q/A风格,所以我不一定需要任何人回答这个问题.这是我学到的东西,许多人可以利用这一点.

我有一个树视图,它包含许多不同的节点.每个节点在其Data属性中都有一个对象,对象从一个主对象列表引用不同级别的层次结构,这些对象非常大(数千个项目).一个节点表示此主列表对象上的特定属性,其中树允许用户选择节点以查看属于该特定所选类别的那些项目.

当树被填充时,它变得非常耗时(在某些情况下为2分钟),因为每个节点需要遍历该大型列表中的每个项目并找到该列表中属于任何给定节点的每个项目.因此,如果此树中有500个节点,则它将遍历此大型列表500次.总共有3个级别的层次结构 - 加载第二和第三级时会出现性能阻塞,但第一级是简单快速的.

现在没有任何选项可以提高数百次迭代这个列表的性能.我想知道是否有任何已知的技巧来提高填充树视图的性能?

以下是它目前的工作原理:

var
  X: Integer;
  N: TTreeNode;
  O: TMyObject;
begin
  for X := 0 to MyObjectList.Count - 1 do begin
    O:= TMyObject(MyObjectList[X]); //Object which Node represents
    N:= TreeView.Items.AddChild(nil, O.Caption);
    N.Data:= O;
    LoadNextLevel(N); //Populates child nodes by iterating through master list again
  end;
end;
Run Code Online (Sandbox Code Playgroud)

并且每个附加级别都有类似的方法.

PS - 第一级层次结构从其自己的单独列表(约50个对象)填充,而第二级和第三级从主列表中的这些数千个对象的属性填充.这就是为什么第一级加载很快,其余的很慢.

Dav*_*ovo 15

如果您真的关心填充大量树视图的速度,您应该查看virtualTreeView(http://code.google.com/p/virtual-treeview/).
它是一个开源树视图,专门设计为虚拟并最大化大型树视图的速度/内存.
这是一个了不起的组件.

  • @Arioch,没办法.还有Joachim Marder,目前负责该项目.他做得对.你在StackOverflow上[`报告一个问题](http://stackoverflow.com/q/19030376/960757)并且你在4天内得到修复:-)我不知道你在哪里听到这个,但是甚至有提供移植到FMX等合同的人.VTV永远不会死!即使这种情况会发生,VTV也足够稳定,即使对于任务,你甚至无法用系统树视图来思考. (3认同)

Jer*_*dge 5

树视图中有一个常见的技巧可以改善这种情况下的性能.刷新此树视图时,仅加载第一级层次结构,并且不必担心任何其他级别.相反,您可以在扩展每个节点时加载每个附加级别.这是怎么做的.

当您填充第一级时,而不是继续加载其每个子节点,而只是创建1个"虚拟"子节点,nilData属性中有一个指针- 因为每个节点都应该在Data属性中有一个对象.然后,监视OnExpanding树视图的事件.展开节点时,它将检查是否存在此"虚拟"子节点.如果是这样,那么它知道它需要加载子节点.

加载第一级层次结构时...

var
  X: Integer;
  N, N2: TTreeNode;
  O: TMyObject;
begin
  for X := 0 to MyObjectList.Count - 1 do begin
    O:= TMyObject(MyObjectList[X]); //Object which Node represents
    N:= TreeView.Items.AddChild(nil, O.Caption);
    N.Data:= O;
    N2:= TreeView.Items.AddChild(N, '');
    N2.Data:= nil; //To emphasize that there is no object on this node
  end;
end;
Run Code Online (Sandbox Code Playgroud)

然后,为OnExpanding... 创建一个事件处理程序

procedure TForm1.TreeViewExpanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean);
var
  N: TTreeNode;
begin
  N:= Node.getFirstChild;
  if N.Data = nil then begin
    //Now we know this is a "dummy" node and needs to be populated with child nodes
    N.Delete; //Delete this dummy node
    LoadNextLevel(N); //Populates child nodes by iterating through master list
  end;
end;
Run Code Online (Sandbox Code Playgroud)

这个技巧的唯一缺点是+,即使可能没有任何子节点,所有尚未展开的节点都会在它们旁边.如果是这种情况,那么当用户单击+以展开节点时,子节点将被删除并+消失,因此用户知道该节点中没有子节点.

此外,使用BeginUpdateEndUpdateTreeView.Items提高,直到它完成所有未执行GUI更新性能...

TreeView.Items.BeginUpdate;
try
  //Refresh the tree
finally
  TreeView.Items.EndUpdate;
end;
Run Code Online (Sandbox Code Playgroud)