7

首先,我正在回答我自己的问题 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 个对象)中填充的,而第二和第三级是从主列表中数千个对象的属性中填充的。这就是为什么第一级加载速度很快,其余的加载速度很慢。

4

2 回答 2

15

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

于 2013-11-13T04:48:32.483 回答
6

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

当您填充第一级时,而不是继续加载其每个子节点,而是只需在其属性中创建一个带有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;

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

此外,使用BeginUpdateand EndUpdateinTreeView.Items通过在全部完成之前不执行 GUI 更新来提高性能......

TreeView.Items.BeginUpdate;
try
  //Refresh the tree
finally
  TreeView.Items.EndUpdate;
end;
于 2013-11-13T01:26:51.220 回答