Jef*_*eff 2 delphi tree virtualtreeview data-structures
正如Rob Kennedy先生所建议的那样,我已经到了需要停止将数据存储在VCL组件中并具有"基础数据结构"的地步.
首先,这个问题是关于"我如何建立基础数据结构".:)
我的层次结构由2级节点组成.
现在,我通过循环根节点来完成我的东西,其中我循环通过rootnode的子节点,以获得我需要的东西(数据).我希望能够将所有数据存储在所谓的底层数据结构中,以便我可以使用线程轻松修改条目(我想我能够做到这一点?)
但是,当循环遍历我的条目(现在)时,结果取决于节点的Checkstate - 如果我使用的是底层数据结构,我怎么知道我的节点是否被检查,当我的数据结构循环通过时,而不是我的节点?
假设我想使用2个级别.
这将是父母:
TRoot = Record
RootName : String;
RootId : Integer;
Kids : TList; //(of TKid)
End;
Run Code Online (Sandbox Code Playgroud)
那孩子:
TKid = Record
KidName : String;
KidId : Integer;
End;
Run Code Online (Sandbox Code Playgroud)
这基本上就是我现在所做的.评论说这不是最好的解决方案,所以我愿意接受建议.:)
我希望你理解我的问题.:)
谢谢!
您要求的数据结构非常简单,它非常简单我建议使用提供的窗口TTreeView:它允许将文本和ID直接存储到树的节点中而无需额外的工作.
尽管我建议使用更简单的TTreeView我将提供我对数据结构问题的看法.首先,我要使用课程,而不是记录.在非常简短的代码示例中,您将以非常不顺畅的方式混合记录和类:当您复制TRoot记录(分配记录制作完整副本,因为记录总是被视为"值")时,您不会制作树的"深层复制":完整的副本TRoot将包含与Kids:TList原始相同的内容,因为类与记录不同,是引用:您正在处理引用的值.
当您拥有包含对象字段的记录时,另一个问题是生命周期管理:记录没有析构函数,因此您需要其他机制来释放拥有的对象(Kids:TList).你可以用a代替TList,array of Tkid但是当你传递怪物记录时你需要非常小心,因为你可能会在最不期望的时候制作大量记录的深层副本.
在我看来,最谨慎的做法是将数据结构基于类,而不是记录:类实例(对象)作为引用传递,因此您可以无需任何问题地移动它们.您还可以获得内置的生命周期管理(析构函数)
基类看起来像这样.您会注意到它可以用作Root或Kid,因为Root和Kid共享数据:两者都有一个名称和一个ID:
TNodeClass = class
public
Name: string;
ID: Integer;
end;
Run Code Online (Sandbox Code Playgroud)
如果此类用作Root,则需要一种方法来存储Kids.我假设您使用的是Delphi 2010+,因此您拥有泛型.这个类有一个列表,如下所示:
type
TNode = class
public
ID: integer;
Name: string;
VTNode: PVirtualNode;
Sub: TObjectList<TNode>;
constructor Create(aName: string = ''; anID: integer = 0);
destructor Destroy; override;
end;
constructor TNode.Create(aName:string; anID: Integer);
begin
Name := aName;
ID := anID;
Sub := TObjectList<TNode>.Create;
end;
destructor TNode.Destroy;
begin
Sub.Free;
end;
Run Code Online (Sandbox Code Playgroud)
您可能没有立即意识到这一点,但仅此类就足以实现多级树!这里有一些代码用一些数据填充树:
Root := TNode.Create;
// Create the Contacts leaf
Root.Sub.Add(TNode.Create('Contacts', -1));
// Add some contacts
Root.Sub[0].Sub.Add(TNode.Create('Abraham', 1));
Root.Sub[0].Sub.Add(TNode.Create('Lincoln', 2));
// Create the "Recent Calls" leaf
Root.Sub.Add(TNode.Create('Recent Calls', -1));
// Add some recent calls
Root.Sub[1].Sub.Add(TNode.Create('+00 (000) 00.00.00', 3));
Root.Sub[1].Sub.Add(TNode.Create('+00 (001) 12.34.56', 4));
Run Code Online (Sandbox Code Playgroud)
您需要一个递归过程来使用以下类型填充虚拟树视图:
procedure TForm1.AddNodestoTree(ParentNode: PVirtualNode; Node: TNode);
var SubNode: TNode;
ThisNode: PVirtualNode;
begin
ThisNode := VT.AddChild(ParentNode, Node); // This call adds a new TVirtualNode to the VT, and saves "Node" as the payload
Node.VTNode := ThisNode; // Save the PVirtualNode for future reference. This is only an example,
// the same TNode might be registered multiple times in the same VT,
// so it would be associated with multiple PVirtualNode's.
for SubNode in Node.Sub do
AddNodestoTree(ThisNode, SubNode);
end;
// And start processing like this:
VT.NodeDataSize := SizeOf(Pointer); // Make sure we specify the size of the node's payload.
// A variable holding an object reference in Delphi is actually
// a pointer, so the node needs enough space to hold 1 pointer.
AddNodesToTree(nil, Root);
Run Code Online (Sandbox Code Playgroud)
使用对象时,虚拟树中的不同节点可能具有与之关联的不同类型的对象.在我们的例子中,我们只需要添加的节点TNode类型,但在现实世界中,你可能有类型的节点TContact,TContactCategory,TRecentCall,都在同一个VT.您将使用is运算符来检查VT节点中对象的实际类型,如下所示:
procedure TForm1.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var PayloadObject:TObject;
Node: TNode;
Contact : TContact;
ContactCategory : TContactCategory;
begin
PayloadObject := TObject(VT.GetNodeData(Node)^); // Extract the payload of the node as a TObject so
// we can check it's type before proceeding.
if not Assigned(PayloadObject) then
CellText := 'Bug: Node payload not assigned'
else if PayloadObject is TNode then
begin
Node := TNode(PayloadObject); // We know it's a TNode, assign it to the proper var so we can easily work with it
CellText := Node.Name;
end
else if PayloadObject is TContact then
begin
Contact := TContact(PayloadObject);
CellText := Contact.FirstName + ' ' + Contact.LastName + ' (' + Contact.PhoneNumber + ')';
end
else if PayloadObject is TContactCategory then
begin
ContactCategory := TContactCategory(PayloadObject);
CellText := ContactCategory.CategoryName + ' (' + IntToStr(ContactCategory.Contacts.Count) + ' contacts)';
end
else
CellText := 'Bug: don''t know how to extract CellText from ' + PayloadObject.ClassName;
end;
Run Code Online (Sandbox Code Playgroud)
以下是将VirtualNode指针存储到节点实例的示例:
procedure TForm1.ButtonModifyClick(Sender: TObject);
begin
Root.Sub[0].Sub[0].Name := 'Someone else'; // I'll modify the node itself
VT.InvalidateNode(Root.Sub[0].Sub[0].VTNode); // and invalidate the tree; when displayed again, it will
// show the updated text.
end;
Run Code Online (Sandbox Code Playgroud)
您知道有一个简单的树数据结构的工作示例.您需要"增长"这个数据结构以满足您的需求:可能性是无穷无尽的!为了给你一些想法,探索方向:
Name:string转换为虚方法GetText:string;virtual,然后创建TNode该覆盖的专用后代GetText以提供专门的行为.TNode.AddPath(Path:string; ID:Integer)允许您执行的操作Root.AddPath('Contacts\Abraham', 1);- 即,自动创建到最终节点的所有中间节点的方法,以允许轻松创建树.PVirtualNode进入TNode本身,所以你可以检查,而该节点是在虚拟树"检查".这将是数据GUI分离的桥梁.| 归档时间: |
|
| 查看次数: |
5632 次 |
| 最近记录: |