Tree Lookup

From Axaptapedia

Standard Axapta uses sometimes a tree to visualize hierarchy.

For example the organization structure in the HR module is a hierarchy. Axapta deploys a tree control both to maintain and manipulate this hierarchy and also to provide a lookup with a tree, when another form/table references the organization table.

There is the tutorial_Form_TreeDatasource form in Axapta demonstrating usage how you can set up ordinary form to show tree view based on datasource

This article describes how such a tree lookup can be implemented:

For the sake of this example, lets assume you have a table called MyHierarchyTable with the following fields:

  Id
  ParentId
  Description

The ParentId field is self referencing to the same table and thus defining the hierarchy. To create our lookup form we do the follwoing:

1) Create a new form MyHierachyLookup and add the table MyHierarchyTable as datasource.

2) Because this is a read-only lookup, we set for this datasource:

  AllowCreate      = No
  AllowDelete      = No
  AllowEdit        = No

3) Now we create a new design. To get a proper Lookup feeling, we set:

  Frame            = None
  HideToolbar      = Yes
  Window Type      = Popup 

4) To the design we add a single tree control called tree with:

  AutoDeclaration  = Yes

5) Now we have to bind the tree to our data and make it behave like a lookup. Most of the work was done by the Axapta programmers already and we can use their class CCFormTreeDatasource, which we declare in the class of our form:

   public class FormRun extends ObjectRun
   {
       CCFormTreeDatasource treeDatasource;   
   }

6) To get better readable code, we create some convinience functions:

The first function is used to initialize the tree. This function is later called from the run method of the form. The second and third function is used to select and expand the tree at a given node. These functions are used to open the lookup initially at the right position. The last function is used to return the selected node on closing the form.

   void InitTree()
   {
       // create datasource for tree
       treeDatasource = new CCFormTreeDatasource(MyHierarchyTable_DS,tree,
                                               fieldnum(MyHierarchyTable,Id),
                                               fieldnum(MyHierarchyTable,ParentId),
                                               fieldnum(MyHierarchyTable,Description),
                                               true, false
                                              );
       // initialize root node
       treeDatasource.initRoot("",,0);
   }
   // wrapper for ExpandAndSelectRec
   // speeds up build of tree, by disabling tree updates
   void ExpandAndSelect(str ChildId)
   {
        tree.lockWindowUpdate(true);
        this.ExpandAndSelectRec(ChildId);
        tree.lockWindowUpdate(false);
   }
   // simple recursive ExpandAndSelect function
   // ok, not so simple, but simpler then the one from Axapta, which was using a stack here
   int ExpandAndSelectRec(str ChildId)
   {
       int node;
       MyHierarchyTable     table;
       str IdTree;
       ;
       // recursively search for parent Ids to be expanded first
       select firstonly table where table.Id == ChildId;
       if (table) {
           node = this.ExpandAndSelectRec(table.ParentId);
            
           // expand found node
           tree.expand(node, FormTreeExpand::EXPAND);
           
           // search for my node starting from parent
           IdTree   = tree.getItem(node).data();
           while (ChildId != IdTree)
           {
            node = tree.getNextVisible(node);
            IdTree   = tree.getItem(node).data();
           }
       } else {
           // if no parent was found, then we reached the root node
           node = tree.getRoot();
       }
       
       // select  and return found node
       tree.select(node);
       return node;
   }
   void exit()
   {
   ;
       // on exit, return data of currently selected node
       element.closeSelect(treedatasource.selectedData());
   }

7) Now we overwrite the run method of the form to open the tree at the right location and preselect any passed node:

   public void run()
   {
       FormControl hostControl;
       ;
       super();
       
       // initialize tree
       this.InitTree();
       
       // if this form was called as lookup, then expand tree at value of calling host control
       hostControl =  element.selectTarget();
       if (hostControl)
           this.ExpandAndSelect( hostControl.valueStr() );
   }

8) We also overwrite the task method of the form to catch any RETURN keys to select a node in our lookup:

   // catch "return" to initiate exit
   int task(int p1)
   {
       int                 ret;
       ;
       ret = super(p1);
       if (p1 == 288)  // Enter
           element.exit();
       Return ret;
   }

9) To avoid any Dynalinks in the datasource, we overwrite the init method of the datasource:

   public void init()
   {
       super();
       this.query().dataSourceNo(1).clearDynalinks();
   }

10) Finally we need to overwrite a couple of event methods from our tree control to achieve the desired behaviour like expanding, selecting and closing with a double click:

   int mouseUp(int x, int y, int button, boolean ctrl, boolean shift)
   {
       super(x, y, button, ctrl, shift);
       
       // must return 1 here, otherwise lookup will close each time on expanding :-;
       return 1;
   }
   // catch double click to initiate exit
   public int mouseDblClick(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift)
   {
       int ret;
       ;
       ret = super(_x, _y, _button, _Ctrl, _Shift);
       
       element.exit(); // exit and return selected node value
       
       return ret;
   }
   void selectionChanged(FormTreeItem oldItem, FormTreeItem newItem, FormTreeSelect how)
   {
       ;
       super(oldItem, newItem, how);
       
       // on select of node in tree, update datasource to fetch new record
       treeDatasource.selectionChanged(oldItem,newItem);
   }
   boolean expanding(int idx, FormTreeExpand action, anytype data)
   {
       boolean ret;
       ;
       ret = super(idx, action, data);
       
       treeDatasource.expanding(idx, action, data); // expand node
       
       Return ret;
   }

Finished! Enjoy your tree lookup,

--Markus Schmitz 06:04, 16 Jun 2005 (EDT)