4

我已经使用 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 的内存。我想知道我使用 VST 的方式在最小化内存使用方面是否不正确?

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

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

4

2 回答 2

11

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

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

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

  • 小的结果集可以一次加载AddChild()或加载到内部数据结构中,然后通过 VT 的事件使用它;
  • 一次加载一个级别可能是树木的良好折衷方案;
  • 如果批量加载非常大的结果集,可能会提供良好的性能和内存使用折衷,但会增加管理 VT 的代码的复杂性。
于 2011-10-02T09:46:01.257 回答
1

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

如果您查看文档,您可以使用该RecNo属性跳转到每条记录,并且可以使用Fields[i](通过它的索引)跳转到每个字段。

TVirtualStringTree使用虚拟模式连接数据库没有任何阻碍。

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

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

于 2011-12-09T08:43:17.037 回答