我一直在使用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活着。以这种方式使用它会有任何记忆效益/性能优势吗?
在上面的情况中,我可以看到网格示例的差异远远小于层次结构(如果有的话) - 因为所有节点在填充时都需要初始化。
答案 0 :(得分:11)
不鼓励AddChild()
的原因是它打破了组件的虚拟范例 - 即使您可能永远不需要它们,也会创建所有节点。假设查询返回100条记录,但树当时显示10个节点。现在,如果用户向下滚动,则浪费了90个节点的资源,因为它们从不需要(不会变得可见)。因此,如果您担心内存使用情况,AddChild()
是个坏主意。另一件事是,如果结果集很大,填充树需要时间,并且您的应用程序当时没有响应。使用虚拟方式(RootNodeCount
和OnInitNode
)时,树立即“准备就绪”,用户不会遇到任何延迟。
然后,如果(相对)使用AddChild()
的小结果集可能是最佳选择 - 它将允许您在一个短事务中加载数据。即在树结构的情况下,当用户扩展父节点时,立即加载整个“级别”是有意义的。
将DB与VT一起使用有点棘手,因为数据库查询也是特殊资源,它有自己的约束(你希望保持事务简短,速度相对较慢,数据库可能只支持单向游标等)。
所以我要说这是你必须根据用例和数据量决定每个案例,即
AddChild()
或加载到内部数据结构中,然后通过VT的事件使用它; 答案 1 :(得分:1)
TQuery提供对记录的类似数组的访问。
如果您查看文档,可以使用RecNo
属性跳转到每条记录,然后使用Fields[i]
(按索引)跳转到每个字段。
使用TVirtualStringTree
作为与数据库连接的虚拟模式没有阻碍。
一旦执行查询,数据在内存中可以作为TDataSet
使用,那么为什么不将它用作数据容器呢?
PS:使用索引访问字段的速度比FieldByName
快。