Each TreeNode has a .Data property. This property is useful, in that it can
be used to associate additional data with a TTreeNode. You could for instance
associate each TreeNode's data property with a Form, and display the associated
form when the TreeNode is clicked, or whatever...
TTreeNode.Data is an untyped pointer, which means it can point to anything. There are a few ways you can use this Data property. I'll demonstrate 3 of them.
{Make Node.Data contain the integer value 1234} Node.Data := pointer(1234); {Get the integer value} iValue := integer(Node.Data);
All I'm doing here is treating the pointer as an integer value. It is extremely
important that you don't try use the TTreeNode.Data as a real pointer in this case.
You will undoubtedly crash your application...
In the example above the .Data property contains the integer value 1234. If you try
use Node.Data as a pointer...... who knows? After all you have no idea what is at
memory address 1234 ($4d2). So just don't do it!
The second method uses classes, this is the method that most people will probably use. Its easy and flexible and there are no pointers to worry about (3rd method).
The reason this method works so well is that Delphi conceals the fact that a class instance is actually a pointer.
var MyClass : TMyClass begin MyClass := TMyClass.Create; MyClass.Free; end;
The MyClass variable is actually a pointer! But thanks to Delphi you never need to worry about that. So how does this help? Well look at this example
Node.Data := TMyClass.Create; TMyClass(Node.Data).Free;
The first line creates an instance of TMyClass and assigns it to the TTreeNode.Data property. The second line type-casts the pointer and frees it. The reason you need to type-cast is that the TTreeNode.Data property is an untyped pointer. Delphi has no idea that you want it to store a TMyClass instance.
The following source is modified from example 1. Here each node has an associated TNodeData class. This class stores that time that the node was created as a string. This is a pretty useless example, but demonstrates how this method works.
Control | Caption | Name |
TButton | Add | but_Add |
TButton | Remove | but_Remove |
TTreeView | tv_eg1 | |
TEdit | ed_Time |
type TNodeData = class sText : string; end; procedure TForm1.but_AddClick(Sender: TObject); var sText : string; begin {If nothing is selected} if( tv_eg1.Selected = nil ) then begin {Does a root node already exist?} if( tv_eg1.Items.Count = 0 ) then begin {Add the root node} with tv_eg1.Items.AddFirst( nil, 'Root' ) do begin Selected := true; {Create the data class} Data := TNodeData.Create; {Set the nodes date time} TNodeData(Data).sText := FormatDateTime( 'hh:nn:ss', now ); end; end else begin {There is a root, so user must first select a node} MessageBeep( -1 ); ShowMessage( 'Select a parent node' ); Exit; end; end else begin {Get a name for the new node} InputQuery( 'New Node', 'Caption ?', sText ); {Add the node as a child of the selected node} with tv_eg1.Items.AddChild( tv_eg1.Selected, sText ) do begin {Create the data class} Data := TNodeData.Create; {Set the nodes date time} TNodeData(Data).sText := FormatDateTime( 'hh:nn:ss', now ); MakeVisible; end; end; end; procedure TForm1.but_RemoveClick(Sender: TObject); begin {Make sure somthing is selected, before trying to delete it} if( tv_eg1.Selected = nil ) then begin MessageBeep( -1 ); ShowMessage( 'Nothing selected' ); Exit; end; {don't allow user to delete the root node} if( tv_eg1.Selected.Level = 0 ) then begin MessageBeep( -1 ); ShowMessage( 'Cant delete the root node' ); Exit; end; {Free the class} if( tv_eg1.Selected.Data <> nil ) then TNodeData(tv_eg1.Selected.Data).Free; {Delete the node} tv_eg1.Selected.Delete; end;
There are a few important lines in the source above
TNodeData = class sText : string; end;Define the class that is going to be associated with each TTreeNode.
Data := TNodeData.Create;Create the TNodeData class and assign it to the Data property
TNodeData(Data).sText := FormatDateTime( 'hh:nn:ss', now );Set the TNodeData's creation time.
if( tv_eg1.Selected.Data <> nil ) then TNodeData(tv_eg1.Selected.Data).Free;Lastly free the memory used by the TNodeData class when it is no longer needed
Finally method 3. This method is very similar to the above one, however instead of using a class I'll use a record. I'll just show the difference between this and method 2...
{The record pointed to by each Node's .data property} prNodeData = ^rNodeData; rNodeData = record sText : string; end;Instead of a class declare a record. Also declare a pointer to the record.
{Allocate memory for the record} Data := New( prNodeData ); {Set the nodes date time} prNodeData(Data)^.sText := FormatDateTime( 'hh:nn:ss', now );First allocate memory for the record. Then set the sText field. Remember to dereferance ( ^ ) the pointer.
{Free the memory used by the record} if( tv_eg1.Selected.Data <> nil ) then Dispose( prNodeData(tv_eg1.Selected.Data) );Free the memory used by the record.
All information on these www pages is copyright (©) 1997 Andre .v.d. Merwe And may not be copied or mirrored without my permission.