We help IT Professionals succeed at work.


ws1999 asked
is there any treeview code sample available?

i want to retrieve data from database and add them to the tree node.

Watch Question

Hi ws19999,

a good article from Delphi3000.com

Upwards and onwards now, to the next chapter in the RCS Tree View series.  This tutorial will focus on populating a treeview with hierarchal records from a table.  For two reasons, I am not including a database with this example.  First, I don't know what databases my potential audience may be using, and second that it serves as an excellent, though cursory, introduction to making your applications data engine-independent.

You will need four fields in your table, and some sample data to be loaded.  This works for any hierarchal list, including a forum/discussion system.  First, you have a unique primary key (integer).  An autonumber or generator value works well for this.  Second is a caption ( the "subject" of your discussion thread ).  Third is an integer indicating the parent thread's primary key.  Fourth is an integer indicating the parent thread's root key (for example, the first thread in the discussion).  I will cover the details of this approach to a hierarchal list in a future article, and will simply leave you with the code to accomplish it in a very efficient manner.

procedure TfrmTreeViewDemo4.FormCreate(Sender: TObject);
// The FormCreate procedure fires as soon as an instantiation of the
//    form is created.  This enables us to setup the default values
//    in the form.
   // The TSession class provides us with a very simple way to get
   //    all of the registered BDE aliases on the current system.
   //    You simply pass a TStrings object to the GetAliasNames
   //    method of the Session, and it will populate the list with
   //    all of the aliases.
   sesDemo.GetAliasNames( cmbAliases.Items );

procedure TfrmTreeViewDemo4.cmbAliasesChange(Sender: TObject);
// This procedure fires whenever an item is selected or unselected
//    in the Aliases combobox.
   // We're going to check to see if an item in the Aliases combo box
   //    is selected.  With any object that uses TStrings or a
   //    decendant class (combobox, listbox, etc.), you can check
   //    the ItemIndex to get the array index of the currently
   //    selected item.  In Delphi, arrays are zero based... thus,
   //    if you had the following items in your combobox:
   //    Apples
   //    Oranges
   //    Pears
   //    Grapes
   //    If "Apples" was selected, then you would have an ItemIndex
   //       equal to 0.  Likewise, "Grapes" would have an ItemIndex
   //       of 3.  If no item in a list is selected, then the
   //       ItemIndex property is equal to -1.
   if ( cmbAliases.ItemIndex > -1 ) then
      // If an item is selected, then we're going to close the
      //    database connection if it's open.
      if ( dbDemo.Connected ) then

      // Next, we're going to set our database component's AliasName
      //    property equal to the value of the Aliases combobox.
      dbDemo.AliasName := cmbAliases.Text;

      // Finally, we will populate the cmbTables combobox with a list
      //    of all tables for the selected alias.  For more
      //    information on the GetTableNames method and what
      //    parameters it requires, consult the Delphi Help.
      dbDemo.Session.GetTableNames( dbDemo.DatabaseName, '', false, false, cmbTables.Items );
      // If no Alias is selected, then we're going to empty the
      //    Tables combobox and close the database connection.

procedure TfrmTreeViewDemo4.cmbTablesChange(Sender: TObject);
// This procedure fires whenever an item is selected or unselected
//    in the Tables combobox.
   // If a Table is selected from the Tables combobox...
   if ( cmbTables.ItemIndex > -1 ) then
      // Set the TTable component's TableName property to the
      //    selected Table.
      tblDemo.TableName := cmbTables.Text;

      // Now, we're going to get a list of fields for the selected
      //    table.  This procedure is similar to GetAliases ( in the
      //    FormCreate procedure ).  We're going to put the results
      //    in the first of four comboboxes, cmbPrimaryKey.
      tblDemo.GetFieldNames( cmbPrimaryKey.Items );

      // Now, instead of re-running the GetFieldNames procedure
      //    to populate the other three combobox components, we're
      //    going to use the Assign method.  The Assign method is
      //    implemented by TPersistent, and basically allows the
      //    copying of properties from one object to another of the
      //    same (or similar) type.  In this way, we can reduce
      //    the processing required to create four copies of the
      //    same list.
      cmbCaption.Items.Assign( cmbPrimaryKey.Items );
      cmbParentId.Items.Assign( cmbPrimaryKey.Items );
      cmbRootId.Items.Assign( cmbPrimaryKey.Items );

procedure TfrmTreeViewDemo4.btnLoadFromDbClick(Sender: TObject);
   function FindNode( Id: Integer ): TTreeNode;
   // The FindNode inline function will search through our TreeView
   //    for an Id value ( specified by the single parameter ), and
   //    will return a pointer to a matching TTreeNode object,
   //    or nil if no match is found.
      // We will need a counter for our loop.
      Counter: Integer;
      // We will initialize the Counter to 0.  It is always a good
      //    idea to initialize variables as well as objects before
      //    any operations try read from them.  In addition, we'll
      //    initialize result to nil.
      Counter  := 0;
      Result   := nil;

      // We're going to loop through the items (nodes) in our
      //    TreeView.
      while ( Counter < tvwDemo.Items.Count ) do
         // We're going to be storing the Primary Key value in the
         //    data property of the TreeNode associated with the
         //    record.  Sound like a mouthful?  Well, it may be,
         //    and if the meaning of that is vague, feel free to look
         //    over it a few times for good measure.  I had to when
         //    I wrote it, just to make sure the logic worked right.
         // On that subject, I really need a better compiler/linker
         //    for my brain... t'would make matters so much easier.
         // At any rate, we're going to check and see if our parameter
         //    Id is equal to the data property of the current node
         //    in the loop.
         if ( Integer( tvwDemo.Items[ Counter ].Data ) = Id ) then
            // If it is, then we're going to return the TTreeNode
            //    as our result, and break out of the while..do loop.
            Result := tvwDemo.Items[ Counter ];

         // If we're still in the loop at this point, we'll move the
         //    counter to the next position.  Rather than use the...
         //    counter := counter + 1
         //    ...statement, it is far faster internally to use
         //    inc( integer ) to increment the value.  For reference,
         //    dec( integer ) will decrement the value.
         inc( Counter )
   // We're going to use a format string for our SQL statement.
   //    The way a format string works is to enter % codes into
   //    your string.  They can be formatted numbers, strings, and
   //    you can do padding and justification as well.  For
   //    more information, do a Help Index Search for "format strings"
   // In this case, we're going to be using indexed values.  Our
   //    format statement, seen below, is going to take an array
   //    of values.  The first four ( 0 - 3 ) are field names,
   //    the fifth value is a table name, and the sixth will be
   //    a decimal value.  We will be using as criteria records
   //    where the field named by the 4th string in the array is
   //    equal to the decimal value.  In addition, we will order the
   //    results by the 3rd string in the array.
   SQL_GET_ITEMS  = 'SELECT %0:s, %1:s, %2:s, %3:s FROM %4:s ' +
                    'WHERE %3:s = %5:d ORDER BY %2:s';
   // We'll need a variable to keep track of the new node being added,
   //    as well as a potential pointer to it's parent.  In addition,
   //    we'll store NewNodeData as a pure pointer.
   NewNode:       TTreeNode;
   NewNodeData:   Pointer;
   ParentNode:    TTreeNode;
      // We're going to use our TQuery component to retrieve the
      //    table values.  In general, Queries should be used instead
      //    of tables, because usually if you are getting back a
      //    single result set of all records in your table, you are
      //    doing something inefficiently.  There are, of course,
      //    exceptions to this rule as with most, but it is a good
      //    guideline to at least understand why you are using a
      //    TTable instead of a TQuery.
      with ( qryDemo ) do
         // We're going to set the TQuery component's SQL statement
         //    from the format constant we defined above.  Using the
         //    string as a template, you can see the array values
         //    that we're passing in, and why they are in that order.
         SQL.Text := Format( SQL_GET_ITEMS, [ cmbPrimaryKey.Text,
                                              StrToInt( edtRootId.Text ) ] );

         // Now, we're going to open the query.

         // Assuming the query executed successfully, we're going to
         //    lock the canvas of our TreeView.  What this will do
         //    is prevent any redraws as new nodes are added or old
         //    ones removed.  Failure to do this can create some very
         //    unprofessional flickering in your TreeView as the
         //    changes take place, and also will increase your load
         //    time significantly.

            // Clear any existing nodes in the TreeView.

            // Loop until we reach the end of the result set of the
            //    TQuery component.
            while not( EOF ) do
               // We're going to turn the integer value of the current
               //    record's primary key into a pointer.  This can
               //    be done because a Pointer is nothing more than
               //    an integer value specifying a memory position.
               //    This knowledge can provide for some interesting
               //    ideas into the ability to directly access
               //    specific parts of memory.
               NewNodeData       := Pointer( FieldByName( cmbPrimaryKey.Text ).AsInteger )

               // We'll set ParentNode to point at the node equal
               //    to the current record's parent id.
               ParentNode := FindNode( FieldByName( cmbParentId.Text ).AsInteger );

               // If no matching node was found...
               if ( ParentNode = nil ) then
                  // We'll add the record as a top-level node, setting
                  //    the caption field as the node's text, and
                  //    our pointer value equal to the primary key.
                  NewNode           := tvwDemo.Items.AddObject( nil, FieldByName( cmbCaption.Text ).AsString, NewNodeData );
                  // If a match was found, then the above code takes
                  //    place with the exception of creating the new
                  //    node as a child of the node that was found.
                  NewNode           := tvwDemo.Items.AddChildObject( ParentNode, FieldByName( cmbCaption.Text ).AsString, NewNodeData );

               // Move to the next record in the query.
            // Whenever you use a BeginUpdate or some sort of lock,
            //    you should always enclose it in a try..finally block
            //    to ensure that the unlock takes place.
      // Close the query.

Best Regards


You have many open questions to which you must return:


To assist you in your cleanup, I'm providing the following guidelines:

1.  Stay active in your questions and provide feedback whenever possible. Likewise, when feedback has not been provided by the experts, commenting again makes them receive an email notification, and they may provide you with further information. Experts have no other method of searching for questions in which they have commented, except manually.

2.  Award points by hitting the Accept Comment As Answer button located above and to the left of that expert's comment.

3.  When grading, be sure to read:
to ensure that you understand the grading system here at EE. If you grade less than an A, you must explain why.

4.  Questions that were not helpful to you should be PAQ'd (stored in the database for their valuable content?even if not valuable to you) or deleted. To PAQ or delete a question, you must first post your intent in that question to make the experts aware. Then, if no experts object after three full days, you can post a zero-point question at community support to request deletion or PAQ. Please include the link(s) to the question(s).
CS:  http://www.experts-exchange.com/jsp/qList.jsp?ta=commspt
At that point, a moderator can refund your points and PAQ or delete the question for you. The delete button does not work.

5.  If you fail to respond to this cleanup request, I must report you to the Community Support Administrator for further action.

Our intent is to get the questions cleaned up, and not to embarrass or shame anyone. If you have any questions or need further assistance at all, feel free to ask me in this question or post a zero-point question at CS. We are very happy to help you in this task!

community support moderator


Question(s) below appears to have been abandoned. Your options are:
1. Accept a Comment As Answer (use the button next to the Expert's name).
2. Close the question if the information was not useful to you. You must tell the participants why you wish to do this, and allow for Expert response.
3. Ask Community Support to help split points between participating experts, or just comment here with details and we'll respond with the process.
4. Delete the question. Again, please comment to advise the other participants why you wish to do this.

For special handling needs, please post a zero point question in the link below and include the question QID/link(s) that it regards.
Please click the Help Desk link on the left for Member Guidelines, Member Agreement and the Question/Answer process.  http://www.experts-exchange.com/jsp/cmtyHelpDesk.jsp

Please click you Member Profile to view your question history and keep them all current with updates as the collaboration effort continues, to track all your open and locked questions at this site.  If you are an EE Pro user, use the Power Search option to find them.

To view your open questions, please click the following link(s) and keep them all current with updates.

To view your locked questions, please click the following link(s) and evaluate the proposed answer.

------------>  EXPERTS:  Please leave any comments regarding your closing recommendations if this item remains inactive another seven (7) days.  Also, if you are interested in the cleanup effort, please click this link http://www.experts-exchange.com/jsp/qManageQuestion.jsp?ta=commspt&qid=20274643
Thank you everyone.
Moderator @ Experts Exchange

P.S.  For any year 2000 questions, special attention is needed to ensure the first correct response is awarded, since they are not in the comment date order, but rather in Member ID order.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.