Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
How to create nodes and subnodes of a TTreeView at runtime to represent a master Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 2.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Tomas Rutkauskas

I would like to present a master - detail relationship within a TTreeView upon 
opening a query at runtime. Getting the correct info is not a problem but I'm 
totally stumped by the use of a TTreeView. Are there commands usable at runtime to 
enable me to create and edit the nodes/ sub nodes of the treeview to present the 
master records as nodes and the detail records as sub nodes within the treeview?


Here's an example of creating / maintaining TreeNodes at runtime. Before I take 
off, I assume that you use database tables like the following:

A MASTERTABLE, with fields M_ID (unique identifier) and M_NAME (a descriptive name);
A DETAILTABLE, with fields D_ID (unique identifier), D_NAME (a descriptive name), 
and D_M_ID (a foreign key value that links the detail record to a master record.)

This could be quite different from what you have, but I need to make assumptions in 
order to write this example.

The first step of the process is to add all master records as parent nodes of 
detail nodes. It goes without saying that I need to add parent nodes first, since 
detail nodes are 'dependent' of them.

You can add all master records to the TreeView by looping the query at runtime and 
do something like this:

{Get all master records in query}
1   { ... }
2   while not qryMasterRecords.EOF do
3   begin
4     {some code will follow here, be patient my friend.}
5     TreeView1.Items.Add(nil, qryMasterRecords.FieldByName('M_NAME').AsString);
6     qryMasterRecords.Next;
7   end;
8   { ... }

The Add method (of a TTreeNodes object) adds the node to the TreeView; the first 
parameter specifies the parent node. (in this case, nil means that it is added as a 
root node.) The second parameter is the node name that is represented in the 
TreeView. (this is the Text property of a TTreeNode.)

However, I am finished yet with the master records. How the heck am I going to 
identify the master nodes when I want to insert detail nodes later on? When adding 
a detail node, I need to know what its parent should be. A bad answer (to me) is to 
say that one can use the node name as an identifier. Since we have a unique 
identifier for each master record in the database, why don't we use it?

The solution to this lies in the Data property of the TreeNode object: This is a 
pointer one can assign to an application-designed object. The intention is to use 
such an object to store the unique identifier of the master record.

Let's use an record-type object like this:

9   type
10    PMaster = ^TMaster
11      TMaster = record
12      Identifier: integer;
13    end;

Assuming these types are used, I modify the master-node-adding code to this:

14  { ... }
15  var
16    MasterPtr: PMaster;
17    { ... }
18    {Get all master records in query}
19    { ... }
20    while not qryMasterRecords.EOF do
21    begin
22      New(MasterPtr);
23      Master^.Identifier := qryMasterRecords.FieldByname('M_ID').AsInteger);
24    TreeView1.Items.AddObject(nil, qryMasterRecords.FieldByName('M_NAME').AsString,
25      MasterPtr);
26    qryMasterRecords.Next;
27  end;
28  { ... }

At runtime, I create a record type object for each record that is found in the 
query. I use a slightly extended version of the Add method. AddObject also links 
MasterPtr with the Data property of the new node.

For now, I have finished with the master nodes: The next step is to add all detail 
nodes. I need to write a small function that searches for a TreeNode with a 
specified M_ID value. I need this while adding detail nodes, because I need to 
identify a node that is the parent node of the detail node that is to be inserted.
30  function SearchMasterNode(iM_ID: integer): TTreeNode;
31  {Searches for a node with a specified M_ID value. Returns the TreeNode that has the
32  specified M_ID value. When it is not found, nil is returned.}
33  var
34    iCount: integer;
35  begin
36    {Default value to return}
37    Result := nil;
38    {For your info: iterating like this loops through all nodes in a TreeView, 
39  including detail nodes}
40    for iCount := 0 to TreeView1.Items.Count - 1 do
41    begin
42      if Assigned(TreeView1.Items.Item[iCount].Data) then
43        if PMaster(TreeView1.Items.Item[iCount].Data)^.Identifier = iM_ID then
44          Result := TreeView1.Items.Item[iCount];
45      {We got a match !}
46    end;
47  end;

From now on, adding detail nodes is much like adding master nodes, with one extra 
move: a search for a parent node.

48  { ... }
49  {Insert all master nodes to the TreeView}
50  { ... }
51  var
52    MasterNode: TTreeNode;
54    {Get all detail records in query}
55    { ... }
56    while not qryDetailRecords.EOF do
57    begin
58      MasterNode := 
59  SearchMasterNode(qryDetailRecords.FieldByName('D_M_ID').AsInteger);
60      {For your info: The Data property of this new node is set to nil.}
61      TreeView1.Items.AddChild(MasterNode,
62  													qryDetailRecords.FieldByName('D_NAME').AsString);
63      qryDetailRecords.Next;
64    end;

The Add method is used here, since I assume that you don't need to identify detail 
nodes for something else. When you do need this (for example, clicking on a detail 
node must result in the representation of detail record data in edit boxes, 
memo-boxes, whatever input control.) use the approach with master nodes.

Finally, to create an application that uses computer memory efficiently, I should 
free all memory used for the record-type objects. I did this by iterating through 
all nodes and freeing the data objects:

65  { ... }
66  var
67    iCount: integer;
68    { ... }
69    for iCount := 0 to TreeView1.Items.Count - 1 do
70    begin
71      if Assigned(TreeView1.Items.Item[iCount].Data) then
72        Dispose(TreeView1.Items.Item[iCount].Data);
73    end;
74    {Finally, free all nodes constructed at runtime}
75    TreeView1.Items.Clear;
76    { ... }

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC