thank you all for your replies!
i have for 10 years been using the tree view using TTreeNode's data property. i wanted to be free of:
- setting the Data property
- creating/destroying the "data" object in a manner so there are no memory leaks
i have used the Data property for an ID number in the past as well.
today, my nodes have GUIDs to find their data in the database so they don't "fit" into the Data property anymore.
using a descendant of TTreeNode seems to have addressed my wishes nicely but in order to make that work nicely i had to do a few things:
- handle TTreeView.OnCreateNodeClass event
- handle TTreeView.OnDeletion event to retrieve latest data from the nodes before they are destroyed
- handle TTreeView.OnAddition event to: 1) maintain a simple list of the nodes 2) set the node's Data property so we can use it to find the place in the list allocated for storing it's data.
here's the code:
TInfoTreeNodeMemory=record
...
end;
TInfoTreeNode=class(TTreeNode)
private
m_rInfoTreeNodeMemory:TInfoTreeNodeMemory;
public
property InfoTreeNodeMemory:TInfoTreeNodeMemory read m_rInfoTreeNodeMemory write m_rInfoTreeNodeMemory;
end;
TInfoTreeNodeMemoryItemList=class
private
m_List:TList<TInfoTreeNodeMemory>;
public
constructor Create;
destructor Destroy; override;
procedure HandleOnDeletion(Node: TInfoTreeNode);
procedure HandleOnAddition(Node: TInfoTreeNode);
end;
TfraInfoTree = class(TFrame)
tvInfo: TTreeView;
procedure tvInfoCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
procedure tvInfoDeletion(Sender: TObject; Node: TTreeNode);
procedure tvInfoAddition(Sender: TObject; Node: TTreeNode);
private
m_NodeMemory:TInfoTreeNodeMemoryItemList;
...
procedure TfraInfoTree.tvInfoCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
begin
// THIS IS VITAL!
NodeClass:=TInfoTreeNode;
end;
procedure TfraInfoTree.tvInfoDeletion(Sender: TObject; Node: TTreeNode);
begin
m_NodeMemory.HandleOnDeletion(TInfoTreeNode(Node));
end;
procedure TfraInfoTree.tvInfoAddition(Sender: TObject; Node: TTreeNode);
begin
m_NodeMemory.HandleOnAddition(TInfoTreeNode(Node));
end;
g_icTreeNodeNotInList=MAXINT;
procedure TInfoTreeNodeMemoryItemList.HandleOnDeletion(Node: TInfoTreeNode);
var
iPosition:integer;
begin
iPosition:=integer(Node.Data);
if iPosition=g_icTreeNodeNotInList then
raise Exception.Create('Node memory not found!')
else
// we recognize this node; store his data so we can give it back to him later
m_List[iPosition]:=Node.InfoTreeNodeMemory;
end;
procedure TInfoTreeNodeMemoryItemList.HandleOnAddition(Node: TInfoTreeNode);
var
iPosition:integer;
begin
// "coat check" for getting back node data later
iPosition:=integer(Node.Data);
if iPosition=g_icTreeNodeNotInList then
begin
// Node.Data = index of it's data
// can't set Node.Data in OnDeletion so we must assign it in OnAddition instead
Node.Data:=pointer(m_List.Count);
// this data may very well be blank; it mostly occupies space; we harvest the real data in OnDeletion
m_List.Add(Node.InfoTreeNodeMemory);
end
else
// we recognize this node; give him his data back
Node.InfoTreeNodeMemory:=m_List[iPosition];
end;
very cool...it meets all my objectives!
to add a node to the tree, all i need to do is:
// g_icTreeNodeNotInList important so the "coat check" (TInfoTreeNodeMemoryItemList)
// can recognize this as something that's not in it's list yet.
MyInfoTreeNode:=TInfoTreeNode(tvInfo.Items.AddChildObject(nParent, sText, pointer(g_icTreeNodeNotInList))));