Performance VirtualStringTree正确/推荐使用

Performance VirtualStringTree正确/推荐使用,performance,delphi,virtualtreeview,tvirtualstringtree,Performance,Delphi,Virtualtreeview,Tvirtualstringtree,我已经使用virtualstringtree一段时间了。我将它用于两种不同的用途,第一种是用于选择和显示数据的普通树,第二种是用于显示SQL语句输出的网格 我加载到树中的所有数据都来自数据库。对于树示例,我有一个parentId字段来区分继承人,对于网格示例,我只需使用一个SQL语句,为每个树定制一条记录(这是唯一的) 我的问题与填充树的首选/最佳方式有关。我从VST文档中读到,您应该将onInitNode事件与rootnodecount一起使用。然而,我发现使用AddChild()方法非常相似

我已经使用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活动。这样使用它会对内存/性能有什么好处吗


在上述情况下,我可以看到网格示例的差异远小于继承者权限(如果有的话),因为所有节点在填充时都需要初始化。

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

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

将DB与VT结合使用有点棘手,因为DB查询也是一种特殊的资源,有其自身的限制(您希望保持事务简短,它相对较慢,DB可能只支持单向游标等)。
所以我想说,这是你必须根据每个用例和数据量来决定的事情,即

  • 小结果集可以使用
    AddChild()
    一次加载所有数据,或者加载到内部数据结构中,然后通过VT的事件使用它
  • 一次加载一个级别对树来说可能是一个很好的折衷方案
  • 对于非常大的结果集,分批加载可能会提供良好的性能和内存使用折衷,但会增加代码管理VT的复杂性

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

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

使用
TVirtualStringTree
作为与数据库连接的虚拟模式,不会有任何阻碍

执行查询后,数据在内存中作为
TDataSet
可用,那么为什么不将其用作数据容器呢


PS:使用字段索引访问字段比使用
FieldByName

+1更快。谢谢您的回答。似乎我的大多数树都应该很好,因为它们只加载了刚刚尝试使用rootnodecount和TQuery的数据。不像我想象的那么简单,因为TQuery不像数组那样提供对记录的随机访问。所以看起来我会坚持我现在得到的,我有一个定制的TQuery控件,类似于我编写的控件,它查询数据库并在内存中保留记录集。我试过使用OnInit,但似乎无法使其工作。在跟踪对OnInit和GetText的调用时,我注意到OnInit被称为2x,然后是GetText。在我的OnInit中,类似TQuery的控件支持->Next();因此,每次调用OnInit时,我都会用数据填充VST Data*,并调用->Next()。我最终得到了一棵重复信息树。还有C++的例子吗?文档中提到了OnInit来调整单元格大小,大部分内容都是在obj-Pascal中。使用如此多内存的主要原因之一是将数据复制到每个节点;如果只存储一个引用(例如ID(示例中的整数),则可以在OnGetText等事件的记录集中查找正确的记录。这样可以节省大量内存并遵循虚拟范例。这是否是