Suppose you want to display customer and their orders in a tree view named custTree. To make the form load faster, you initially add only the first order for each customer (so that customer with orders will have the + button), then load all other orders on-demand through the canExpand event. You call the following method in the form’s overridden open( ) method so that the tree is initialized before the form opens:

function initTree

   local tCust 

   if form.rowset.first() 

      do 

         tCust = new TreeItem( form.custTree, ; 

         "C" + form.rowset.fields[ "CUST_ID" ].value ) 

         tCust.demandLoaded = false // Create new properties 

         tCust.bookmark = form.rowset.bookmark() 

         with tCust 

            text = form.rowset.fields[ "LAST_NAME" ].value.rightTrim() + "," + ; 

            form.rowset.fields[ "FIRST_NAME" ].value.rightTrim() 

            if not form.orders1.rowset.endOfSet 

               with new TreeItem( tCust, ; 

               "O" + form.orders1.rowset.fields[ "ORDER_NUM" ].value ) 

                  text = form.orders1.rowset.fields[ "ORDER_NUM" ].value + " " + ; 

                  form.orders1.rowset.fields[ "ORDER_DATE" ].value 

               endwith 

            endif 

         endwith 

      until not form.rowset.next() 

   endif 

Unique names are generated for each level of tree items and passed as the second parameter to the TreeItem class constructor; duplicate names are not allowed. The customer and order rowsets are linked with masterRowset so that navigation in the customer rowset automatically navigates to the corresponding orders. The text property is assigned inside a WITH block in case other stock properties are assigned in the future, which would also go inside the WITH block. The custom demandLoaded and bookmark properties for the top-level tree item must be created outside the WITH block; you can’t create properties in a WITH block. Here is the canExpand event handler:

function CUSTTREE_canExpand

   local t, r 

   t = this.selected // TreeItem being expanded 

   r = form.orders1.rowset // Detail rowset 

   if not t.expanded and not t.demandLoaded 

      form.rowset.goto( t.bookmark ) 

      do while r.next( ) // Start with second detail row (if any) 

         with new TreeItem( t, "O" + r.fields[ "ORDER_NUM" ].value ) 

            text = r.fields[ "ORDER_NUM" ].value + " " + ; 

            r.fields[ "ORDER_DATE" ].value 

         endwith 

      enddo 

      t.demandLoaded := true 

   endif 

return true 

The event handler creates some short-hand variables for the tree item being expanded and the order rowset. It then checks to see if the tree item is being expanded (it is if its expanded property is false to begin with) and has not been demand-loaded yet. If so, it goes to the saved bookmark and immediately tries to go to the second matching order. By calling next( ) at the top of the DO WHILE loop, nothing happens if there is only one matching order, and that’s all that is needed to loop through all the matching orders.

After adding the rest of the orders, the custom demandLoaded property is set to true so that this code is skipped the next time this customer’s tree item is expanded. Finally, the event handler always returns true to allow the expansion (or collapse, if that’s what triggered the event).

Note that this technique would not work in a situation where the first detail row might change while the user is viewing the tree. In that case, when the detail rows are demand-loaded, the row that was loaded when then tree was initialized would be loaded again, causing an error with the duplicate name. But if, for example, the orders are stored chronologically, this could not happen.