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

时间:2013-11-13 01:26:51

标签: performance delphi treeview 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;

每个额外级别的类似方法。

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

2 个答案:

答案 0 :(得分:15)

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

答案 1 :(得分:5)

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

填充第一级时,而不是继续加载其每个子节点,而只是在其nil属性中创建一个带有Data指针的“虚拟”子节点 - 因为每个节点期望在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;

然后,为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;

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

此外,在BeginUpdate中使用EndUpdateTreeView.Items可以通过不执行GUI更新来提高性能,直到全部完成...

TreeView.Items.BeginUpdate;
try
  //Refresh the tree
finally
  TreeView.Items.EndUpdate;
end;