Tree Lookup

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)