VirtualStringTree正确/推荐使用

时间:2011-10-02 02:52:06

标签: performance delphi virtualtreeview tvirtualstringtree

我一直在使用virtualstringtree一段时间了。我将它用于两个不同的东西,首先是一个普通的树,用于选择,显示数据,其次是一个网格,用于显示SQL语句的输出。

我加载到树中的所有数据都来自数据库。对于树示例,我有一个parentId字段来区分层次结构,对于网格示例,我只使用带有每个树的自定义记录的SQL语句(这是唯一的)。

我的问题与填充树的首选/最佳方式有关。我从VST文档中读到,您应该使用onInitNode事件和rootnodecount。但是我发现使用AddChild()方法非常相似,即使不鼓励它。

让我展示一些(简化的)例子:

1。层次结构

type PData = ^rData;
    rData = packed record
      ID : Integer;
      ParentID : Integer;
      Text : WideString;
    end;

procedure Loadtree;
 var Node : PVirtualNode;
Data : PData;
 begin
    Q1 := TQuery.Create(Self);
            try
                Q1.SQL.Add('SELECT * FROM Table');
            Q1.Open;
            Q1.Filter := 'ParentID = -1'; //to get the root nodes
            Q1.Filtered := True;
            while not Q1.Eof do
            begin
                    Node := VST.AddChild(nil);
                    Data := VST.GetNodeData(Node);
                    Data.ID := Q1.Fields[fldID].AsInteger;
                    Data.ParentID := Q1.Fields[fldParentID].AsInteger;
                    Data.Text := Q1.Fields[fldText].AsString;
                    //now filter the query again to get the children of this node
                    PopulateChildren(Data.ParentID,Node); //add children to this node and do it recursively
                    Q1.Next;
            end;
       finally
          Q1.free;
      end;

end;

2。格

procedure LoadGrid;
var Node : PVirtualNode;
Data : PData;
begin
     Q1 := TQuery.Create(self);
        try
            Q1.SQL.Add('SELECT * FROM Table');
            Q1.Open;
            while not Q1.eof do
            begin
                    Node := VST.AddChild(nil);
                    Data.ID := Q1.Fields[fldID].AsInteger;
                    Data.Text := Q1.Fields[fldText].AsString;
                    Q1.Next;
            end;
    finally
            Q1.Free;
    end;
end;

所以基本上我绕过了RootNodeCount和OnInitNode方法/属性,并使用旧式的方式向树中添加节点。它似乎工作正常。请注意,在示例中,我在运行时创建并销毁我的查询。

我开始以这种方式使用树的原因是我可以加载树中的所有数据,然后在我使用完之后释放TQuery。我在想,不管保持TQuery活着/创建,我仍然需要使用我的rData记录来存储数据,因此如果我没有销毁TQuery就会占用更多内存。目前我的应用程序在完全加载时使用大约250 + MB,并且当我运行SQL报告并在VST中显示它们时可以增加。我已经看到它在运行带有20000多个节点和50多列的SQL报告时使用了大约1GB的ram。我想知道我使用VST的方式是否与最小化内存使用有关?

我会更好地为树的生命周期创建查询并使用onInitNode事件吗?因此,当树请求数据时,它使用onInitNode / OnInitChildren事件(即树的纯虚拟范例)从TQuery中获取数据?因此,我需要在表单的持续时间内保持TQuery活着。以这种方式使用它会有任何记忆效益/性能优势吗?

在上面的情况中,我可以看到网格示例的差异远远小于层次结构(如果有的话) - 因为所有节点在填充时都需要初始化。

2 个答案:

答案 0 :(得分:11)

不鼓励AddChild()的原因是它打破了组件的虚拟范例 - 即使您可能永远不需要它们,也会创建所有节点。假设查询返回100条记录,但树当时显示10个节点。现在,如果用户向下滚动,则浪费了90个节点的资源,因为它们从不需要(不会变得可见)。因此,如果您担心内存使用情况,AddChild()是个坏主意。另一件事是,如果结果集很大,填充树需要时间,并且您的应用程序当时没有响应。使用虚拟方式(RootNodeCountOnInitNode)时,树立即“准备就绪”,用户不会遇到任何延迟。

然后,如果(相对)使用AddChild()的小结果集可能是最佳选择 - 它将允许您在一个短事务中加载数据。即在树结构的情况下,当用户扩展父节点时,立即加载整个“级别”是有意义的。

将DB与VT一起使用有点棘手,因为数据库查询也是特殊资源,它有自己的约束(你希望保持事务简短,速度相对较慢,数据库可能只支持单向游标等)。
所以我要说这是你必须根据用例和数据量决定每个案例,即

  • 小结果集可以一次性加载AddChild()或加载到内部数据结构中,然后通过VT的事件使用它;
  • 按时加载一个级别可能是树木的妥协;
  • 如果非常大的结果集批量加载可能会提供良好的性能和内存使用损失,但会增加管理VT的代码的复杂性。

答案 1 :(得分:1)

TQuery提供对记录的类似数组的访问。

如果您查看文档,可以使用RecNo属性跳转到每条记录,然后使用Fields[i](按索引)跳转到每个字段。

使用TVirtualStringTree作为与数据库连接的虚拟模式没有阻碍。

一旦执行查询,数据在内存中可以作为TDataSet使用,那么为什么不将它用作数据容器呢?

PS:使用索引访问字段的速度比FieldByName快。