- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我已经到了需要停止将数据存储在 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 中添加 TContact
、 TContactCategory
、 TRecentCall
类型的节点。您将使用 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/
我有一个 VirtualTreeView,它最初隐藏了一些列(coVisible 不存在)。 启用它们(将 coVisible 添加到选项列)后,发生了一件奇怪的事情 - 列出现但与前一列重叠。我可以
如何配置虚拟 TreeView 以在用户按 TAB 时编辑下一列? +--------+-----------+ + |1 + |2 + +--------+----------
如何使用 VirtualTreeView 组件检查节点是否可见(在屏幕上)?像这样的事情: if not Grid.NodeVisible (Node) then Grid.ScrollInto
有没有办法使用virtualtreeview实现无限滚动? 我想一次加载一定数量的数据库记录,并在用户向下滚动时将它们添加到 virtualtreeview 中。但我不确定如何触发添加新行。 最佳答案
我正在尝试使用 TButton 创建节点。我创建节点和链接到节点的按钮。在事件 TVirtualStringTree.AfterCellPaint 上,我初始化按钮上的 BoundsRect。但按钮始
我有一个节点列表。我想添加拖放重新排列功能,但我不知道如何执行此操作。 我尝试使用 TVirtualStringTree 的 OnDragDrop 事件,但我无法弄清楚。我查看了文档,遗憾的是没有用于
我正在尝试在 TVirtualStringTree 中创建一个类似于以下内容的 View : 在上面的示例中,我展示了一些我想要达到的可能场景。 FolderA 具有粗体文本,其后的同一节点中的红色非
对于那些自己没有遇到过这个问题的人来说,这个问题似乎是显而易见的。 我需要处理 VTV 中的选择更改。我有一个简单的节点列表。我需要对所有当前选定的节点进行操作 用户点击节点; 用户按住 Shift/
我正在将一个节点从一个应用程序拖动到另一个应用程序。仅当我之前选择节点时它才能正常工作。这是因为我使用 GetNodeData(FocusedNode) 方法收集数据。 我想以某种方式在节点悬停时自动
我依靠 VirtualTreeView 来显示数千个项目,这些项目必然会偶尔发生变化,当发生这种情况时,树会被清理并再次填充。 排序是自动完成的(设置了 toAutoSort 标志),但这会产生递归初
我想将根添加到 VirtualTreeView http://www.delphi-gems.com/index.php/controls/virtual-treeview像这样的线程: functi
我正在使用 Cosmin Prund 提供的代码在这个post因为它符合我的需要,但是我经常遇到内存泄漏,我无法弄清楚如何释放它的节点 TNode包含 TObjectList 的对象反过来,最后一个也
我的应用程序将通过虚拟节点循环并检查它们的数据。我正在使用另一种表单来执行此操作,而不是包含 VirtualStringTree 的表单。 (我有我的理由;)) 我的问题是:如何将这些节点+它们的数据
我需要以编程方式在 VirtualTreeview 中选择特定节点,但我没有这样做找到任何方法来做到这一点。任何人都可以提供解决方案吗? 最佳答案 像这样选择节点: VirtualTree.Selec
是否有事件通知有关单击 VirtualTreeView 标题列复选框的事件?这是这张图片上突出显示的复选框: 最佳答案 为 OnHeaderClick 事件编写处理程序,并检查 HitInfo 参数的
我有包含 3 列的 VirtualTreeView(如果重要的话,列标题是不可见的)。 当我按 F2(启动编辑器的默认键)编辑节点时,它会编辑第 0 列中的节点。如何将其切换为编辑第 1 列中的节点?
我使用此代码来填充 VirtualStringTree 并允许重命名项目: //------------------------------------------------------------
我开始使用 VirtualTreeView,从所有网络信息和文档看来,VirtualTreeView 管理用户提供的数据的内存(如果有记录)。无需 New() 和 Dispose() 任何用户数据。只
使用VirtualTreeView时拖动操作默认为[doCopy,doMove]。移动操作由带有小框的箭头指针指示,复制操作由相同的指针图标指示,但旁边添加了 [+]。 默认情况下,VT 使用复制操作
我想更改现有虚拟(根)节点的单元格文本。我可以更改数据,但我必须刷新整个 TreeView,以便再次触发 GetText。是否有更简单的方法或可能的方法来仅刷新 1 个根节点而不是整个树? 感谢您的帮
我是一名优秀的程序员,十分优秀!