First of all you will need a form with a control of the type FormTreeControl. Set the Autodeclaration property to yes. In this guide we assume, that the control is named “tree”.
A tree consists of a number of FormTreeItems. Each FormTreeItem has these visual attributes:
This icon can be either a checkmark or an empty box. Set or get the value with the following method:
To use these icons the checkbox property, must be set to yes. It is only possible to use these two icons, they cannot be changed. If you need other icons, use stateImage or image. (see below).
This icon can be anything you desire. All you need to do is create an imagelist (a class that extends ImagelistAppl).
During initialization you must call:
Assuming that your imagelist is named imagelist.
Use this method to set/get the image:
Where #myImage is a macro defining the image number. These can be seen using the form: Tutorial_resource.
Hint: ImageListAppl_Checkbox is a useful imagelist. It contains all sorts of checkmarks – including the grayed one.
During initialization you must call:
Where #myImage is a macro defining the image number. These can be seen using the form: Tutorial_resource.When the item is selected in the tree, the image displayed will be the one set with:
Typically image and selectedImage will be set to the same.
The difference between image and stateimage, is that image allows overlays on the icon. An overlay is a small extra icon like the padlock in the AOT. They can be set with:
The text is the text displayed in the tree. Set or get it with this method:
It has a maximum length at 250 characters defined by Windows. If the user should be able to rename the text, set the editlabels property to yes.
Now we are ready to start building the tree. Assuming the data needed to build the tree is stored in the temporary table TmpTree, the source for creating the tree could look like this:
void buildTree(int parentKey, TreeItemIdx parentIdx)
while select tmpTreeTemp
where tmpTreeTemp.parent == parentKey
formTreeItem = new FormTreeItem();
idx = tree.addItem(parentIdx, 0, formTreeItem);
This small recursive method will do it all for you – but it requires that you have all your data stored and ready.
The most annoying part of building trees is that not all values can be set during build. You will have to use getItem and setItem (see below) to set state values such as stateImage and stateBold.
If you have a huge tree, this approach is not optimal. Instead the tree could be build as the user expands the branches. If this performance approach is used, you will need to specify if the formTreeItem has children manually and you will need to override the expanding method on the tree:
As default the FormTreeItem can be expanded, if it contains children. If you want it to be expandable before building the children, you can use the method:
Calling this method with -1 will set the value back to auto.
To build the tree as the user expands his way, the expanding method must be overriden on the tree.
public boolean expanding(TreeItemIdx _idx, FormTreeExpand _action, anytype _data)
FormTreeItem item = this.getItem(_idx);
ret = super(_idx, _action, _data);
Notice the use of the StateExpandedOnce method. It will ensure that the same branch not is build more than once. To use this approach the buildTree method must also be modified to be non-recursive.
In this example the data method is used to store context information.
Often the tree is a picture of some physical information. To get from the tree and back to physical information the data method can be used.
The data method can hold any basic data type (int, string, enum) and classes. In the example above the data() holds the unique identifier of the record in the TmpTree table.
If more than one basic data types are needed, several approaches can be used:
Using the temporary table will speed up performance, because you do not need any method calls to access the data. However, since the data method is incapable of holding records, a small workaround must be used:
Map map = new Map(Types::Integer, Types::Record);
Table = item.data().lookup(0);
This approach gave 5 times performance compared to using a class as a struct in the security system in version 3.0. Mostly due to the fact the context information was needed each time the tree was redrawn.
The tree is very easy to navigate using these methods on the tree:
FormTreeItem GetItem(TreeItemIdx idx)
Returns the FormTreeItem given an idx.
Set the item again. To be used when states has been changed. Must be used in conjunction with get.
TreeItemIdx GetChild(TreeItemIdx idx)
Returns the first child under a branch.
TreeItemIdx GetNextSibling(TreeItemIdx idx)
Returns the next child given its sibling (brother/sister).
TreeItemIdx GetParent(TreeItemIdx idx)
Returns the parent of a branch.
Returns the selected item. If the SingleSelection property is set to yes.
Returns the first selected item. If the SingleSelection property is set to no.
Returns the next selected item, given the previous.
To loop over the selected items, this code can be used:
TreeItemIdx idx = tree.getFirstSelected();
idx = tree.getNextSelected(idx);
You can get very precise information on where the user clicks in the tree, if you override the mouseDown method:
int mouseDown(int x, int y, int button, boolean ctrl, boolean shift)
[idx,f] = this.hitTest(x,y);
FormTreeItem = this.getItem(idx);
return super(x, y, button, ctrl, shift);
In the #FormTreeControl macro bit patterns matching where the user can click are defined. The example above will catch if he clicks on the image.
Now you should have all the tools to get started. This guide has only covered a small part of forest. With Axapta form trees you do exactly the same as seen in other Windows applications. For more examples see the tutorial_form_treecontrol form and the SysFormTreeItem class.