问题
I have a list of objects populated from a third-party project file. The way this file was designed is so each item is on a "level" of hierarchy. So the very first item is on level 0, all of its child items are on level 1, and so on.
As an example:
1. Node 1 (Level 0)
2. Node 1.1 (Level 1)
3. Node 1.2 (Level 1)
4. Node 1.3 (Level 1)
5. Node 1.3.1 (Level 2)
6. Node 1.4 (Level 1)
7. Node 2 (Level 0)
8. Node 2.1 (Level 1)
9. Node 2.1.1 (Level 2)
10. Node 3 (Level 0)
This would produce a hierarchy like so:
- Node 1
--- Node 1.1
--- Node 1.2
--- Node 1.3
----- Node 1.3.1
--- Node 1.4
- Node 2
--- Node 2.1
----- Node 2.1.1
- Node 3
My issue is figuring out how to populate this structure into a VCL TTreeView
based on these "Level" properties of each listed object. If I had designed this third-party file structure, I would have used a parent property instead of a level property.
Objects in this list can be iterated like this:
var
I: TMyItem;
N: TTreeNode;
begin
for X := 0 to MyList.Count - 1 do begin
I := MyList[X];
//TMyItem has property "Level" which specifies hierarchy
// as well as "Title" property for the node's caption
//How to create node based on Level?
N.Data := I;
end;
end;
Based on this structure, how do I populate this in a tree view?
回答1:
Try something like this:
var
Item: TMyItem;
Node: TTreeNode;
NodeLevel: Integer;
X: Integer;
begin
Node := nil;
NodeLevel := 0;
for X := 0 to MyList.Count-1 do
begin
Item := MyList[X];
if (Node = nil) or (Item.Level <= 0) then
begin
Node := TreeView1.Items.AddObject(nil, Item.Text, Item);
NodeLevel := 0;
end
else if Item.Level = NodeLevel then
begin
Node := TreeView1.Items.AddObject(Node, Item.Text, Item);
end else
begin
while Item.Level <= NodeLevel do
begin
Node := Node.Parent;
Dec(NodeLevel);
end;
Node := TreeView1.Items.AddChildObject(Node, Item.Text, Item);
Inc(NodeLevel);
end;
// set Node properties as needed...
end;
end;
回答2:
Create a list that will contain the latest parent node for each level. Initially this list will be empty.
Now walk the linear list. Whenever you add an item at level N to the list, you do the following:
- Add it as a child of the latest parent at level N-1, and
- set the latest parent at level N to be the item you just added.
You'll need to handle the case where N=0 for which the above algo would require you to come up with the latest parent at level -1, whatever that means. Does your tree have an overall root node? If so, then by definition, the latest parent at level -1 is the root node. Otherwise, if there's no root node, you'll need do whatever your tree view component requires to add a node at the top level, as opposed to as a child of another node.
Assuming that there is no root node, then the code would look like this:
var
Item: TItem;
LatestParents: TList<TTreeNode>;
Parent, NewNode: TTreeNode;
begin
LatestParents := TList<TTreeNode>.Create;
try
LatestParents.Add(nil);
for Item in Items do
begin
Parent := LatestParents[Item.Level];
NewNode := TreeView.Items.AddChild(Parent, Item.Text);
LatestParents.Count := Max(LatestParents.Count, Item.Level+2);
LatestParents[Item.Level+1] := NewNode;
end;
finally
LatestParents.Free;
end;
end;
You may want to put some error checking into the code if there's a possibility of your code encountering a malformed description of the tree. For instance if the first node that you encounter does not have level 0, then this code will fail.
来源:https://stackoverflow.com/questions/19772367/how-to-populate-a-tree-view-based-on-a-flat-list-with-levels