VirtualStringTree正确/推荐使用 [英] VirtualStringTree Correct/recommended use

查看:606
本文介绍了VirtualStringTree正确/推荐使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我的所有数据加载到树是从数据库。对于树示例,我有一个parentId字段来区分父系,对于网格示例,我只需使用一个SQL语句与每个树的自定义记录(这是唯一的)。



我的问题涉及填充树的首选/最佳方式。我从VST文档读取,你应该使用onInitNode事件以及rootnodecount。但是,我发现使用AddChild()方法非常相似,即使不鼓励。



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



1。 Heirarchy

  type PData = ^ rData; 
rData =打包记录
ID:整数;
ParentID:整数;
文本:WideString;
结束

程序Loadtree;
var Node:PVirtualNode;
数据:PData;
begin
Q1:= TQuery.Create(Self);
try
Q1.SQL.Add('SELECT * FROM Table');
Q1.Open;
Q1.Filter:='ParentID = -1'; //获取根节点
Q1.Filtered:= True;
而不是Q1.Eof do
begin
Node:= VST.AddChild(nil);
数据:= VST.GetNodeData(Node);
Data.ID:= Q1.Fields [fldID] .AsInteger;
Data.ParentID:= Q1.Fields [fldParentID] .AsInteger;
Data.Text:= Q1.Fields [fldText] .AsString;
//现在再次过滤查询以获取此节点的子节点
PopulateChildren(Data.ParentID,Node); //将子节点添加到此节点并递归执行
Q1.Next;
结束
finally
Q1.free;
结束

end;

2。网格

  procedure LoadGrid; 
var节点:PVirtualNode;
数据:PData;
begin
Q1:= TQuery.Create(self);
尝试
Q1.SQL.Add('SELECT * FROM Table');
Q1.Open;
而不是Q1.eof do
begin
Node:= VST.AddChild(nil);
Data.ID:= Q1.Fields [fldID] .AsInteger;
Data.Text:= Q1.Fields [fldText] .AsString;
Q1.Next;
结束
finally
Q1.Free;
结束
结束

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



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



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



在上述情况下,我可以看到网格示例的差异将远远小于(如果任何)比由于所有节点需要在填充时被初始化的事实。

解决方案

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



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



使用DB与VT有点棘手,数据库查询也是特殊的资源,它有自己的约束(你想保持事务短,相对较慢,数据库可能只支持单向游标等)。


所以我想说,这是你必须根据用例和数据量来决定每一个案例,即




  • 小结果集可以用 AddChild()一次加载到内部数据结构中,然后通过VT的事件使用它;

  • 加载一个级别对于树木来说可能是很好的妥协;

  • 如果批量加载非常大的结果集可能会提供良好的性能和内存使用妥协,但增加了管理VT的代码的复杂性。


I have been using virtualstringtree for a while now. I use it for two different things, first a s a normal tree for selecting ,displaying data and secondly as a grid to show outputs from SQL statements.

All my data loaded in to the trees is from a database. For the tree example, i have a parentId field to distinguish the heirarchy, and for the grid examples i simply use an SQL statement with a customized record for each tree (that is unique).

My questions relates to the preferred/best way to populate the tree. I have read from the VST docs that you should use the onInitNode event along with rootnodecount. However i have found that using the AddChild() method to be very similar, even though it is not encouraged.

Let me show some (simplified) examples:

1. Heirarchy

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. Grid

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;

So essentially i am bypassing the RootNodeCount and OnInitNode methods/property and using the old fashioned way to add nodes to the tree. It seems to work fine. Note in the example i create and destroy my queries at runtime.

The reason i started using the tree in this manner is that i could load all the data in the tree once and then free the TQuery after i had finished using it. I was thinking that regardless of keeping the TQuery alive/created i would still need to use my rData record to store the data, hence using up more memory if i did not destroy the TQuery. Currently my application uses about 250+MB when fully loaded, and can increase when i run SQL reports and display them in the VST. I have seen it use about 1GB of ram when i have run a SQL report with 20000+ nodes and 50+ columns. What i would like to know if the way i am using the VST incorrect with regards to minimizing memory usage?

Would i be better off creating a query for the lifetime of the tree and using the onInitNode event? So that when data is requested by the tree it fetches it from the TQuery using the onInitNode/OnInitChildren event (i.e. the pure virtual paradigm of the tree)? Thus i would need to keep the TQuery alive for the duration of the form. Would there be any memory benefit/performance benefit in using it this way?

In the above situations the i can see the difference for the grid example would be far less (if any) than the heirarchy - due to the fact that all nodes need to be initialized when populated.

解决方案

The reason why AddChild() is not encouraged is that it breaks the virtual paradigm of the component - you create all the nodes, even though you may never need them. Say the query returns 100 records but the tree shows 10 nodes at the time. Now if the user nevers scrolls down you have wasted resources for 90 nodes as they are never needed (don't became visible). So if you're worried about memory usage, AddChild() is bad idea. Another thing is that if the resultset is big, populating the tree takes time and your app isn't responsive at that time. When using virtual way (RootNodeCount and OnInitNode) the tree is "ready" immediately and user doesn't experience any delay.

Then again, in case of (relatively) small resultsets using AddChild() might be the best option - it would allow you to load data in one short transaction. Ie in case of tree structure it would make sense to load whole "level" at once when user expands the parent node.

Using DB with VT is a bit tricky as the DB query is special resource too, with it's own constraints (you want to keep transactions short, it is relatively slow, the DB might only support one way cursor etc).
So I'd say it is something you have to decide on each individual case, depending on the use case and amount of data, ie

  • small resultset is OK to load all at once with AddChild() or into internal data structure and then using it throught VT's events;
  • loading one level at time is probably good compromise for trees;
  • in case of very large resultsets loading in batches might give good perfomance and memory usage compromise but adds complexity to the code managing VT.

这篇关于VirtualStringTree正确/推荐使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆