Delphi - 大量节点下的虚拟字符串树慢速 GetText 方法
我对 TVirtualStringTree 组件还不太有经验,因此也许我忽略了一些琐碎的事情。
我的应用程序将文件信息收集到记录中(文件名、路径、大小),并在虚拟字符串树中显示数据。
现在,当有很多节点(200K+)时,我会经历严重的减速,整个树基本上滞后。我知道仅记录数据就占用了相当大的内存,但我发现延迟是由VST的OnGetText方法引起的。 因此,无论该方法读取实际数据还是将 CellText 设置为静态字符串(例如 CellText := 'Test';),速度都会显着下降。 如果我退出 OnGetText 而不设置 CellText,它就可以正常工作 - 即使我的树中有多达 1,000,000 个节点。 另外,如果我折叠树(FullCollapse)以这种方式隐藏 90% 的节点,OnGetText 的行为也可以,或者至少更好。
据我了解,仅对实际可见的屏幕节点调用 OnGetText,因此我不明白为什么对于树中的大量节点来说这是一个问题。
有人有任何提示可以给我指明方向吗?
编辑:
德尔福版本:D2010 VST版本:4.8.6
我的最简单测试形式的代码基本上如下:
var
SkipGetText : boolean;
procedure TXForm.VSTGetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
if SkipGetText then exit;
CellText := 'TEST';
// actual code commented out to reduce complications
end;
如果我设置CellText,它会滞后,如果我退出,它不会。 奇怪的是,我越向下滚动,情况就会变得更糟。
以下是分配为 NodeData 的内容:
type
PVSData = ^Fi;
Fi = Packed Record
Name, Dir, Ext: String;
Size: Int64;
end;
procedure TXForm.AddFile( const RootFolder:string; const SR: TSearchRec );
var
FileInfo: PVSData;
FileSize: Int64;
Node: PVirtualNode;
begin
Node := VST.AddChild(nil);
INC(AllFiles);
FileInfo := VST.GetNodeData(Node);
FileInfo^.Name := SR.Name;
FileInfo^.Dir := RootFolder;
Int64Rec(FileSize).Hi := SR.FindData.nFileSizeHigh;
Int64Rec(FileSize).Lo := SR.FindData.nFileSizeLow;
FileInfo^.Size := FileSize;
end;
procedure TXForm.VSTPaintText(Sender: TBaseVirtualTree;
const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
begin
if SkipPaintText then exit;
case ListView.GetNodeLevel(Node) of
0: TargetCanvas.Font.Color := Color1;
else TargetCanvas.Font.Color := Color2;
end;
end;
procedure TXForm.VSTBeforeCellPaint(Sender: TBaseVirtualTree;
TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
begin
case ListView.GetNodeLevel(Node) of
0: TargetCanvas.Font.Color := Color1;
else TargetCanvas.Font.Color := Color2;
end;
end;
我注意到,扩展/折叠和重新扩展似乎以某种方式改善了情况,但我无法解释为什么这实际上会产生任何影响。
I am not yet very experienced with the TVirtualStringTree component, therefore maybe I overlooked something trivial.
My app gathers File Information into a record (FileName, Path, Size) and displays the data in a Virtual String Tree.
Now when there are lots of Nodes (200K+) I experience a heavy slow down, the whole Tree basically lags. I am aware that the memory footprint is quite large with just the record data alone, but I found out that the lag is caused by the OnGetText method of the VST.
Hereby it doesn't matter if the method reads actual data or sets the CellText to an static string (e.g. CellText := 'Test';) the slow down is significant.
If I exit OnGetText without setting CellText, it works fine - even with as much as 1,000,000 Nodes in my Tree.
Also, If I collapse the Tree (FullCollapse) hiding this way 90% of my Nodes, OnGetText behaves ok as well or at least much better.
As far as I understand it, the OnGetText is only called for actually visible On Screen Nodes, therefore I don't get why this is such an issue with large amounts of Nodes in the Tree.
Anybody has any hints for me to point me in a direction?
EDIT:
Delphi Version: D2010
VST Version: 4.8.6
My code in its simplest test form is basically as follows:
var
SkipGetText : boolean;
procedure TXForm.VSTGetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
if SkipGetText then exit;
CellText := 'TEST';
// actual code commented out to reduce complications
end;
If I set CellText, it lags, if I exit, it doesn't.
Strange enough, it gets worse the further I scroll down.
Here's what's assigned as NodeData:
type
PVSData = ^Fi;
Fi = Packed Record
Name, Dir, Ext: String;
Size: Int64;
end;
procedure TXForm.AddFile( const RootFolder:string; const SR: TSearchRec );
var
FileInfo: PVSData;
FileSize: Int64;
Node: PVirtualNode;
begin
Node := VST.AddChild(nil);
INC(AllFiles);
FileInfo := VST.GetNodeData(Node);
FileInfo^.Name := SR.Name;
FileInfo^.Dir := RootFolder;
Int64Rec(FileSize).Hi := SR.FindData.nFileSizeHigh;
Int64Rec(FileSize).Lo := SR.FindData.nFileSizeLow;
FileInfo^.Size := FileSize;
end;
procedure TXForm.VSTPaintText(Sender: TBaseVirtualTree;
const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
begin
if SkipPaintText then exit;
case ListView.GetNodeLevel(Node) of
0: TargetCanvas.Font.Color := Color1;
else TargetCanvas.Font.Color := Color2;
end;
end;
procedure TXForm.VSTBeforeCellPaint(Sender: TBaseVirtualTree;
TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
begin
case ListView.GetNodeLevel(Node) of
0: TargetCanvas.Font.Color := Color1;
else TargetCanvas.Font.Color := Color2;
end;
end;
I noticed, that expanding / collapsing and re-expanding somehow seems to improve the situation, but it's beyond me to tell why this could actually have any impact.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果任何列是自动调整大小的,则控件需要知道所有节点值的宽度才能确定最大值。
If any of your columns are auto-sized, then the control needs to know the widths of all the nodes' values in order to determine the maximum.
奇怪的是,我认为VST的整个设计只是加载活动视图中节点的cellnodes,而不是整个树。您确定这不是代码中未显示的其他因素,例如为每个节点执行 fileexists 等操作吗?
Strange, I thought it was the whole design of VST to only load cellnodes for the nodes in the active view, not the entire tree. Are you sure it isn't some other factor in the code that you don't show, like doing a fileexists or so for every node?
问题解决了。事实证明,删除节点时可能会出现问题。不是删除父节点的所有子节点,而是仅删除父节点。我预计子节点也会自动删除,但是当我将代码更改为首先删除子节点然后删除父节点时,滞后消失了。现在我可以毫无延迟地将一百万个文件名加载到树中。
Problem solved. It turns out, there might have been a complication while deleting nodes. Instead of deleting all children of a parent node, only the parent node has been removed. I expected the childnodes to be removed automatically as well, but when I changed the code to first delete children then the parent node, the lagging vanished. Now I can load a million file names to the tree without lag.
你没有说你使用的是哪个版本的Delphi。在 D2009 之前的版本中,
TVirtualTreeView
使用WideString
字符串类型,这种类型通常本质上很慢,因为它没有引用计数、写时复制语义AnsiString
有,因此请尽量减少字符串操作。在 D2009 及更高版本中,TVirtualTreeView
使用较新的UnicodeString
字符串类型而不是WideString
。You did not say which version of Delphi you are using. In versions prior to D2009,
TVirtualTreeView
uses theWideString
string type, which is inherently slow in general as it does not have the reference-counting, copy-on-write semantics thatAnsiString
has, so try to minimize your string operations as much as possible. In D2009 and later,TVirtualTreeView
uses the newerUnicodeString
string type instead ofWideString
.