Reputation: 1621
I want to write a program that works on a hierarchically organized tree data structure. I need to have a root node and then be able to add child nodes and to move these node across the complete tree.
Is there a data structure in Delphi I can use for my algorithms and then later simply "copy" it to the GUI using a TTreeview
component?
I suppose I need such a class:
type
TMyElement = class(...)
ElementName: String;
...
ChildElementList: TMyElementList;
HasChildElement: Boolean;
...
end;
At the dots, there will be more fields and routines specific for the element. How to build up this class? And how can I conveniently add these elements to a TreeView
, using the ElementName
field as caption for the tree nodes?
Upvotes: 1
Views: 2516
Reputation: 43669
So, you have two questions:
How to build up this class?
Well, not yet focusing on the viewing-in-a-tree-view-ability, you could descent your class from TObjectList
. Doing so, every element is capable of holding more of these object lists = elements. This approach is for example used for TMenuItem
too.
And how can I conveniently add these elements to a
TreeView
, using theElementName
field as caption for the tree nodes?
You could add an AssignTo
method to the class so that you can "assign" the root node to the TreeView
.
Here you have an example implementation of the whole class. Add own properties as needed.
type
TElement = class(TObjectList)
private
FName: String;
FParent: TElement;
function GetItem(Index: Integer): TElement;
function GetLevel: Integer;
procedure SetItem(Index: Integer; Value: TElement);
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
public
function Add(AElement: TElement): Integer;
procedure AssignTo(Dest: TPersistent);
constructor Create; overload;
constructor Create(AName: String); overload;
constructor Create(AParent: TElement); overload;
constructor Create(AParent: TElement; AName: String); overload;
destructor Destroy; override;
function HasChilds: Boolean;
procedure Insert(Index: Integer; AElement: TObject);
property Items[Index: Integer]: TElement read GetItem write SetItem;
default;
property Level: Integer read GetLevel;
property Name: String read FName write FName;
end;
{ TElement }
function TElement.Add(AElement: TElement): Integer;
begin
if AElement = nil then
Result := -1
else
Result := inherited Add(AElement);
end;
procedure TElement.AssignTo(Dest: TPersistent);
var
Nodes: TTreeNodes;
Node: TTreeNode;
I: Integer;
begin
if Dest is TTreeView then
AssignTo(TTreeView(Dest).Items)
else if Dest is TTreeNodes then
begin
Nodes := TTreeNodes(Dest);
Nodes.BeginUpdate;
Nodes.Clear;
for I := 0 to Count - 1 do
begin
Node := Nodes.AddNode(nil, nil, Items[I].Name, Items[I], naAdd);
Items[I].AssignTo(Node);
end;
Nodes.EndUpdate;
end
else if Dest is TTreeNode then
begin
Node := TTreeNode(Dest);
Nodes := Node.Owner;
for I := 0 to Count - 1 do
Items[I].AssignTo(
Nodes.AddNode(nil, Node, Items[I].Name, Items[I], naAddChild));
end;
end;
constructor TElement.Create;
begin
Create(nil, '');
end;
constructor TElement.Create(AName: String);
begin
Create(nil, AName);
end;
constructor TElement.Create(AParent: TElement);
begin
Create(AParent, '');
end;
constructor TElement.Create(AParent: TElement; AName: String);
begin
inherited Create(True);
FName := AName;
if AParent <> nil then
AParent.Add(Self);
end;
destructor TElement.Destroy;
begin
if FParent <> nil then
FParent.Extract(Self);
inherited Destroy;
end;
function TElement.GetItem(Index: Integer): TElement;
begin
Result := TElement(inherited Items[Index]);
end;
function TElement.GetLevel: Integer;
begin
if FParent = nil then
Result := 0
else
Result := FParent.Level + 1;
end;
function TElement.HasChilds: Boolean;
begin
Result := Count > 0;
end;
procedure TElement.Insert(Index: Integer; AElement: TObject);
begin
if AElement <> nil then
inherited Insert(Index, AElement);
end;
procedure TElement.Notify(Ptr: Pointer; Action: TListNotification);
begin
inherited Notify(Ptr, Action);
case Action of
lnAdded:
TElement(Ptr).FParent := Self;
lnExtracted,
lnDeleted:
TElement(Ptr).FParent := nil;
end;
end;
procedure TElement.SetItem(Index: Integer; Value: TElement);
begin
inherited Items[Index] := Value;
end;
Example usage:
procedure TForm2.FormCreate(Sender: TObject);
begin
FRoot := TElement.Create;
TElement.Create(FRoot, '1');
TElement.Create(FRoot, '2');
TElement.Create(FRoot[1], '2.1');
TElement.Create(FRoot[1], '2.2');
TElement.Create(FRoot[1][0], '2.1.1');
TElement.Create(FRoot[1][0][0], '2.1.1.1');
TElement.Create(FRoot[1][0][0], '2.1.1.2');
TElement.Create(FRoot[1], '2.3');
TElement.Create(FRoot, '3');
TElement.Create(FRoot, '4');
TElement.Create(FRoot[3], '4.1');
FRoot.AssignTo(TreeView1);
end;
Filling a TTreeView
at runtime is great fun!
Upvotes: 2
Reputation: 769
Google 'rmtreenonview'. It is a single unit which implements a non-visual TTreeView-like class--most of the methods and properties are fully compatible with TTreeView.
rmtreenonview is quick and lightweight...we use it for manipulating a bunch of tree-structured data, but write the results to a grid control for display. But, you could copy results to a TTReeView just as easily.
Oh, and it also has hash-based tree searching capability and a couple other additons.
Upvotes: -2