gpt4 book ai didi

delphi - 树状数据结构(与 VirtualTreeview 一起使用)

转载 作者:行者123 更新时间:2023-12-03 15:37:44 27 4
gpt4 key购买 nike

我已经到了需要停止将数据存储在 VCL 组件中的地步,并拥有一个“底层数据结构”,如 Mr. Rob Kennedy suggested.

首先,这个问题是关于“如何制作底层数据结构”。 :)

我的层次结构由 2 级节点组成。

现在,我通过循环根节点来遍历我的东西,其中我循环遍历根节点的子节点,以获得我需要的东西(数据)。我希望能够将所有数据存储在所谓的底层数据结构中,以便我可以使用线程轻松修改条目(我想我能够做到这一点?)

但是,当循环访问我的条目时(现在),结果取决于节点的 Checkstate - 如果我使用底层数据结构,我如何知道我的节点是否被检查,当它是我的数据结构时循环通过,而不是我的节点?

假设我想使用 2 个级别。

这将是父级:

TRoot = Record
RootName : String;
RootId : Integer;
Kids : TList; //(of TKid)
End;

还有 child :

TKid = Record
KidName : String;
KidId : Integer;
End;

这基本上就是我现在所做的。评论指出这不是最好的解决方案,因此我愿意接受建议。 :)

我希望您能理解我的问题。 :)

谢谢!

最佳答案

您请求的数据结构非常简单,我建议使用 Windows 提供的 TTreeView :它允许将文本和 ID 直接存储到树的节点中,无需额外的工作。

<小时/>

尽管我建议使用更简单的 TTreeView,但我仍将提供我对数据结构问题的看法。首先,我将使用,而不是记录。在非常短的代码示例中,您以一种非常不幸的方式混合记录和类:当您复制 TRoot 记录时(分配记录会生成完整的副本,因为记录始终被视为“值”),您不会制作树的“深拷贝”:TRoot 的完整副本将包含与原始版本相同的 Kids:TList,因为类与记录不同,是引用:您正在复制引用的值。

当您拥有带有对象字段的记录时,另一个问题是生命周期管理:记录没有析构函数,因此您需要其他机制来释放拥有的对象(Kids:TList )。您可以将 TList 替换为 array of Tkid,但是在传递怪物记录时您需要非常小心,因为您可能会在您最意想不到的时候结束对巨大记录的深层复制。

在我看来,最谨慎的做法是将数据结构基于,而不是记录:类实例(对象)作为引用传递,因此您可以在所有地方移动它们想要没有问题。您还可以获得内置的生命周期管理(析构函数)

基类看起来像这样。您会注意到它可以用作 Root 或 Kid,因为 Root 和 Kid 共享数据:两者都有名称和 ID:

TNodeClass = class
public
Name: string;
ID: Integer;
end;

如果这个类被用作根,它需要一种方法来存储 child 。我假设你使用的是 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;

您可能不会立即意识到这一点,但仅这个类就足以实现多级树!下面是一些用一些数据填充树的代码:

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));

您需要一个递归过程来使用此类型填充虚拟 TreeView :

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);

使用对象时,虚拟树中的不同节点可能具有与其关联的不同类型的对象。在我们的示例中,我们仅添加 TNode 类型的节点,但在现实世界中,您可能会在一个 VT 中添加 TContactTContactCategoryTRecentCall 类型的节点。您将使用 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;

以下是为什么将 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;
<小时/>

您知道有一个简单的树数据结构的工作示例。您需要“增长”此数据结构以满足您的需求:可能性是无限的!为您提供一些想法和探索方向:

  • 您可以将 Name:string 转换为虚拟方法 GetText:string;virtual,然后创建 TNode 的专门后代来重写 GetText 以提供专门的行为。
  • 创建一个 TNode.AddPath(Path:string; ID:Integer),它允许您执行 Root.AddPath('Contacts\Abraham', 1); - 即自动创建所有中间节点到最终节点的方法,以便轻松创建树。
  • PVirtualNode 包含到 TNode 本身中,以便您可以检查节点是否在虚拟树中“检查”。这将成为数据与 GUI 分离的桥梁。

关于delphi - 树状数据结构(与 VirtualTreeview 一起使用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5365365/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com