Delphi中填充VirtualStringTree的理想方法是什么?

3

我正在使用 Delphi 2010 并且已经有一段时间了,开始使用 VirtualTreeView (精确地说是 VirtualStringTree)...但似乎我在做某些事情时出现了问题,因为它们并不像我期望的那样工作。

我正在尝试用指向记录中存储的文件/子文件夹描述的节点填充我的 VST,这些记录是通过扫描用户提供的路径生成的...(更多细节显示在以下图片中)

enter image description here

如所示,节点以奇怪的方式显示..无论我做什么,节点数据都没有被正确初始化。对于“文件”列的节点标题是唯一正常工作的。

这是我使用的代码:

1- 节点数据声明:

type  nodeData=record
            Text, Size, Path:String;
            ImageIndex:Integer;
           end;

  PNodeData=^nodeData;


var hashmap:TDictionary<String, PVirtualNode>; // hashmap-> to store parent nodes (Folder)
    filesList:TDictionary<Integer,nodeData>; // fileList to store records of data

2-方法

a) 扫描由用户指定的路径

procedure AddAllFilesInDir(const Dir:String);
begin
// it scans the path 'Dir' and extract the "name & size" of each file/folder found in this dir
end;

b) 生成 filesList 字典... 图像存储在 "treeImageLIst" 中,该字典链接到 treeview.images 属性。

procedure addFileToList(Name, Size:String);
var d:nodeData;
    parent:String;
    SHFileInfo :TSHFileINfo;
    Icon:TIcon;
begin
parent:=ExtractFileDir(Name);

//Get The Icon That Represents The File/Folder
SHGetFileInfo(PChar(Name), 0, SHFileInfo,  SizeOf(SHFileInfo),
                SHGFI_ICON or SHGFI_SMALLICON );


Icon := TIcon.Create;
Icon.Handle := SHFileInfo.hIcon;

// set The Name, Size, Path
d.Name:=ExtractFileName(Name);
d.Size:=Size;
d.Path:=parent;

// set the ImageIndex
d.ImageIndex:=Form1.treeImageList.AddIcon(Icon);

// add the node to fileList
filesList.Add(filesList.Count, d);

// Destroy the Icon
DestroyIcon(SHFileInfo.hIcon);
Icon.Free;
end;

c) 创建名为"theTree"的树

procedure createTree();
var theNode, Node:PVirtualNode;
    d:PNodeData;
    parent:String;
    nData:nodeData;
    i:integer;
begin

for i := 0 to filesList.Count - 1 do
begin

 nData:=filesList.Items[i];  parent:=nData.Path;

 if(hashmap.ContainsKey(parent))then theNode:=hashmap.Items[parent]
 else theNode:=nil;

 Node:=Form1.theTree.AddChild(theNode);

 // add a checkbox and make it checked
 Node.CheckType:=ctCheckBox;
 Node.CheckState:=csCheckedNormal;

 // get the newly created node data
 d:=Form1.theTree.GetNodeData(Node);

 // assign a data  to the newly created node
 d^:=nData;

 // add the node to hashmap if it's a new folder node
 if((ExtractFileExt(nData.Text)='')and(not hashmap.ContainsKey(nData.Path+'\'+nData.Text)))
 then hashmap.Add(nData.Path+'\'+nData.Text, Node);

end;

 Form1.theTree.Expanded[Form1.theTree.TopNode]:=True;
end;

d) 树形视图事件

procedure TForm1.theTreeFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var d:PNodeData;
begin
  d := Sender.GetNodeData(Node);
  Finalize(d^);
end;


procedure TForm1.theTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
var d:PNodeData;
begin
 d:=Sender.GetNodeData(Node);

 case Column of
  0:CellText:=d^.Text;
  1:CellText:=d^.Size;
 end;
end;


procedure TForm1.theTreeGetImageIndex(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  var Ghosted: Boolean; var ImageIndex: Integer);

var d:PNodeData;
begin
d:=Sender.GetNodeData(Node);

if(Kind in [ikNormal, ikSelected])then
begin
 if(Column=0)then ImageIndex:=d^.ImageIndex;
end;

end;

我现在非常沮丧,不知道为什么节点不能正确创建。虽然我测试了记录数据并且它们被成功创建,但是当我测试 onNodeClick 事件时,我发现由节点指向的数据记录只返回第一个字段,而其他字段要么为空,要么会生成一个 Access violation exception 异常。


4
在虚拟控件中,你并不是真正地“填充(populate)”一个控件。虚拟控件并不为其节点保存数据,而是在需要显示节点信息时才请求数据。要让虚拟树工作,你只需要设置其根节点数量即可。有一个使用从单独的数据结构获取数据的 TVirtualStringTree 的示例,可以在 我的回答中了解如何使多个节点显示相同数据 - Marjan Venema
5
如果将大小乘以一个(看似)任意的数字5解决了您的问题,那么我建议您再仔细检查一下指向数据类型的指针和SizeOf函数的知识。SizeOf(SomePointerType)返回的是指针的大小,而不是指针所指向的内容的大小。因此,您通过乘以5使一切正常应该告诉您,现在您正在告诉VST要预留足够的内存来保存记录。这再次告诉您应该使用节点记录的大小,而不是记录类型的指针的大小。也就是说:theTree.NodeDataSize:=SizeOf(nodeData)(注意“P”字母的缺失)。 - Marjan Venema
1
如果记录只包含一个整数,则其大小与指针相同(至少在Win32中)。 - Uwe Raabe
2
你找到的教程几乎肯定是错的。有时候人们会对节点数据的存储位置感到困惑。如果他们认为自己需要分配空间,那么他们通常会这样做,并将数据的指针存储在GetNodeData区域中。如果您有一个包含节点数据的记录,则应始终使用NodeDataSize := SizeOf(TNodeData) - Rob Kennedy
1
我甚至费了些心思查看了演示中的源代码。Minimal 项目在 Main.pas 中提供了一个非常简单的正确使用它的示例。http://code.google.com/p/virtual-treeview/source/browse/trunk/Demos/Minimal/Main.pas - Ken White
显示剩余8条评论
1个回答

1
你发布的代码过多,这不利于提问。每天都有数百个问题被发布,其中很多没有代码示例或者代码示例不足。而你却做了相反的事情,远远超出了正常范围。
“填充”虚拟树的最佳方法是不要填充它。相反,只需设置rootNodeCount,然后在必要时逐层设置子节点的计数。对于折叠的节点,仅知道其是否有子节点即可。一旦展开子节点,您可以填充子元素。
请注意,当您按照Virtual Controls的方式进行操作时,您不需要编写大量的代码,而只需“回答有关模型对象基础状态的问题”。有多少个根节点?您告诉虚拟树那个数字。然后从那里开始,当它询问您时,您只需简单地回答问题。在根节点下,第3个节点的第0列的文本是什么?(它会调用一个事件,您处理并返回该信息)。请注意,初始化DATA是VirtualTreeViews的新手常常误解的事情。理想情况下,DATA应该包含指向真实模型对象的指针,以便可以回答VirtualTreeView提出的问题。回答这些问题的最有效的类甚至可能不需要为树中每个可见节点实例化真实模型对象,尽管这是可以接受和常见的。重要的是,您了解到这并非绝对必要。

其次,如果您的目标是使用TVirtualTreeView来模拟shell资源管理器,那么已经有了相应的代码,可以查看VirtualTreeview网站上的“Advanced”VT演示的子部分。请参阅此处我指出的选项卡:

enter image description here


正如我在这里所说的那样,我知道我没有以最完美的方式使用VST..但是时间对我来说是一个重要的因素..而且对于你指出的演示..我之前看过它,对于从未使用过VST的人来说似乎太过复杂。 - DZkid
2
除非您理解虚拟控件使用的模型和思想,否则您将无法快速工作。 - Warren P
但是我现在不需要虚拟控件,我只需要一个比Delphi自带的TTreeView更好的TreeView。 - DZkid
2
然后获取另一个树形视图。试图将虚拟控件转换为非虚拟控件会让你发疯。我告诉你,我已经花费了100多个小时来学习VirtualTreeView。如果你想要一个易于学习的非虚拟树形视图,比TTreeView更好(我想你是指更漂亮?),你需要购买一个,因为没有免费的易于使用的非虚拟树形视图适用于Delphi。TTreeView和TJvTreeView都是Windows公共控件树形视图的包装器。 - Warren P
我所说的“更好”是指功能,而不是仅仅让它看起来漂亮。我需要VST在接下来的几天内完成这个任务 - DZkid
我从未免费找到过非虚拟多列控件,它结合了Grid(列)和Tree(嵌套)功能于一个控件中。尽管VirtualTreeView是一个很棒的组件,但它并不简单,而且你不是唯一发现学习其中的技巧困难的人。 - Warren P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接