This article is dedicated to my colleague Andrew Nearness
This example shows how to create a “Simple and create Detail – Tree” Form
This is the result we want to obtain

First create Extended data types
Al0VendorActivityId of type Int64

Al0VendorActivityName of type String

Al0VendorActivityHierarchyId of type Int64

Then create “Al0VendorActivity” table
Add these fields :
- VendorActivity
- VendorActivityName
- IsVendorActivity
- ParentVendorActivityId
- VendorActivityHierarchy

Insert a new field group, name it NameDescription and add “VendorActivityName” field inside

Add this code to the table
public class AL0VendorActivity extends common { //Al0_0270_VendorPortal - BEGIN /// <summary> /// Find by RecId /// </summary> public static AL0VendorActivity find(Al0VendorActivityId _al0VendorActivityId, boolean _forUpdate = false) { AL0VendorActivity aL0VendorActivity = null; if (_al0VendorActivityId) { aL0VendorActivity.selectForUpdate(_forUpdate); select firstonly * from aL0VendorActivity where aL0VendorActivity.RecId == _al0VendorActivityId; } return aL0VendorActivity; } //Al0_0270_VendorPortal - END //Al0_0270_VendorPortal - BEGIN /// <summary> /// Give a default name to the tree node /// </summary> /// <param name = "_hierarchyRecId"></param> /// <returns></returns> public static Al0VendorActivityName findNonExistNameInHierarchy(Al0VendorActivityHierarchyId _hierarchyRecId) { return AL0VendorActivity::findNonExistByString( tableNum(Al0vendorActivity), fieldNum(Al0vendorActivity, VendorActivityHierarchy), _hierarchyRecId, fieldNum(Al0vendorActivity, vendorActivityName), extendedTypeNum(Al0vendorActivityname), "@SYS134268", "@SYS136676" ); } //Al0_0270_VendorPortal - END //Al0_0270_VendorPortal - BEGIN /// <summary> /// Give a default name to the tree node /// </summary> /// <param name = "_tableId"></param> /// <param name = "_matchingFieldId"></param> /// <param name = "_matchingFieldValue"></param> /// <param name = "_strFieldId"></param> /// <param name = "_strFieldType"></param> /// <param name = "_strValueExactMatch"></param> /// <param name = "_strValueFormatter"></param> /// <param name = "_excludedRecId"></param> /// <returns></returns> public static str findNonExistByString( tableId _tableId, fieldId _matchingFieldId, anytype _matchingFieldValue, fieldId _strFieldId, extendedTypeId _strFieldType, str _strValueExactMatch, str _strValueFormatter, recId _excludedRecId = 0 ) { DictTable dictTable = new DictTable(_tableId); Common common = dictTable.makeRecord(); int idx = 0; anytype result, firstStringValue; int edtMaxLen = new DictType(_strFieldType).stringLen(); result = _strValueExactMatch; do { select firstonly crossCompany common where common.(_strFieldId) == result && common.(_matchingFieldId) == _matchingFieldValue && common.RecId != _excludedRecId ; if (!common.RecId) { break; } // we must detect infinite loop: if (1 == idx) { firstStringValue = common.(_strFieldId); } else if ((idx > 0) && (firstStringValue == common.(_strFieldId))) { throw error( strFmt("@SYS330468", _tableId, tableId2name(_tableId), _matchingFieldId, fieldId2name(_tableId, _matchingFieldId), _matchingFieldValue, _strFieldId, fieldId2name(_tableId, _strFieldId), idx, firstStringValue ) ); } idx++; result = strFmt(_strValueFormatter, idx); // trim as necessary if (strLen(result) > edtMaxLen) { result = subStr(result, 1, edtMaxLen); } } while (true); return result; } //Al0_0270_VendorPortal - END }
Create a Form and apply the “Simple and create Detail – Tree”



Insert “ActivityLookup” group under “FormTreeGroupControl” and add a new Reference Group under it

Name it “ActivityFindReferenceGroup”





Override these 4 form methods


Now add this code to the Form class
[Form] public class Al0VendorActivity extends FormRun { CCFormTreeDatasource treeDatasource; /// <summary> /// Run event /// </summary> public void run() { FormControl hostControl; super(); this.InitTree(); } /// <summary> /// InitTree event /// </summary> void InitTree() { treeDatasource = new CCFormTreeDatasource(Al0VendorActivity_ds, Tree,fieldnum(Al0VendorActivity,RecId), fieldnum(Al0VendorActivity,ParentVendorActivityId), fieldnum(Al0VendorActivity,VendorActivityname), false, false ); // Create First Record treeDatasource.initRoot("@Al0AM:Al0_0270_VendorPortal_01",1,0); } /// <summary> /// ExpandAndSelectRec event /// </summary> /// <param name = "ChildId"></param> /// <returns></returns> int ExpandAndSelectRec(Al0VendorActivityId ChildId) { int node; AL0VendorActivity TreeItemGroupTable; Al0VendorActivityId IdTree; select firstonly TreeItemGroupTable where TreeItemGroupTable.RecId == ChildId; if (TreeItemGroupTable) { node = this.ExpandAndSelectRec(TreeItemGroupTable.ParentVendorActivityId); tree.expand(node, FormTreeExpand::EXPAND); IdTree = tree.getItem(node).data(); while (ChildId != IdTree) { node = tree.getNextVisible(node); IdTree = tree.getItem(node).data(); Al0VendorActivity al0VendorActivityCurr = AL0VendorActivity::find(IdTree); str treeNodeNewText = al0VendorActivityCurr.VendorActivity + "," + al0VendorActivityCurr.VendorActivity; FormTreeItem formTreeItem = tree.getItem(node); formTreeItem.text(treeNodeNewText); tree.setItem(formTreeItem); } } else { node = tree.getRoot(); } tree.select(node); return node; } /// <summary> /// Expand and select /// </summary> /// <param name = "ChildId"></param> void ExpandAndSelect(Al0VendorActivityId ChildId) { tree.lockWindowUpdate(true); this.ExpandAndSelectRec(ChildId); tree.lockWindowUpdate(false); } /// <summary> /// /// </summary> void exit() { element.closeSelect(treedatasource.selectedData()); } /// <summary> /// Intercept System buttons onclick() events /// </summary> /// <param name = "_taskId"></param> /// <returns></returns> public int task(int _taskId) { #Task int ret; switch (_taskId) { case #taskNew: this.insertTreeNode(); break; case #taskDeleteRecord: this.deleteTreeNode(); break; default: ret = super(_taskId); } return ret; } /// <summary> /// Delete tree node /// </summary> void deleteTreeNode() { AL0VendorActivityId ItemGroup; ItemGroup = AL0VendorActivity.ParentVendorActivityId; ////////////////////////////////////////////////////////////// //Delete Tree Node treeDatasource.delete(); ////////////////////////////////////////////////////////////// element.refreshTree(ItemGroup); } /// <summary> /// Insert tree node /// </summary> void insertTreeNode() { AL0VendorActivity insItemGroupTable; int idx = Tree.getSelection(); //Get current selected record AL0VendorActivity selectedTreeItem = AL0VendorActivity::find(Tree.getItem(idx).data()); Al0VendorActivityHierarchyId al0VendorActivityHierarchyId = 1; ////////////////////////////////////////////////////////////// //Create Tree Node insItemGroupTable.clear(); if(selectedTreeItem) { insItemGroupTable.ParentVendorActivityId = selectedTreeItem.RecId; } else { insItemGroupTable.ParentVendorActivityId = al0VendorActivityHierarchyId; } insItemGroupTable.VendorActivity = AL0VendorActivity::findNonExistNameInHierarchy(al0VendorActivityHierarchyId); insItemGroupTable.VendorActivityName = AL0VendorActivity::findNonExistNameInHierarchy(al0VendorActivityHierarchyId); insItemGroupTable.IsVendorActivity = NoYes::Yes; insItemGroupTable.VendorActivityHierarchy = al0VendorActivityHierarchyId; insItemGroupTable.insert(); ////////////////////////////////////////////////////////////// element.refreshTree(insItemGroupTable.RecId); } /// <summary> /// Refresh Tree event /// </summary> /// <param name = "_ItemGroupId"></param> void refreshTree(Al0VendorActivityId _ItemGroupId) { Al0VendorActivity_ds.query().dataSourceName("Al0VendorActivity").clearRanges(); element.InitTree(); element.ExpandAndSelect(_ItemGroupId); } [Control("ReferenceGroup")] class ActivityFindReferenceGroup { /// <summary> /// /// </summary> /// <returns></returns> public Common lookupReference() { Common ret; ret = super(); return ret; } /// <summary> /// /// </summary> /// <returns></returns> public boolean modified() { boolean ret; ret = super(); if (treeDatasource) { element.expandAndSelect(ActivityFindReferenceGroup.value()); } return ret; } /// <summary> /// /// </summary> /// <returns></returns> public Common resolveReference() { Common ret; ret = super(); return ret; } } [Control("Tree")] class Tree { /// <summary> /// Expanding event /// </summary> /// <param name = "_Idx"></param> /// <param name = "_action"></param> /// <param name = "_data"></param> /// <returns></returns> boolean expanding(int idx, FormTreeExpand action, anytype data) { boolean ret; ret = super(idx, action, data); treeDatasource.expanding(idx, action, data); // expand node Return ret; } /// <summary> /// mouseUp event /// </summary> /// <param name = "_x"></param> /// <param name = "_y"></param> /// <param name = "_button"></param> /// <param name = "_Ctrl"></param> /// <param name = "_Shift"></param> /// <returns></returns> public int mouseUp(int x, int y, int button, boolean ctrl, boolean shift) { super(x, y, button, ctrl, shift); return 1; } /// <summary> /// MouseDblClick event /// </summary> /// <param name = "_x"></param> /// <param name = "_y"></param> /// <param name = "_button"></param> /// <param name = "_Ctrl"></param> /// <param name = "_Shift"></param> /// <returns></returns> public int mouseDblClick(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift) { int ret; ret = super(_x, _y, _button, _Ctrl, _Shift); element.exit(); return ret; } /// <summary> /// Selection changed event /// </summary> /// <param name = "_OldItem"></param> /// <param name = "_NewItem"></param> /// <param name = "_how"></param> public void selectionChanged(FormTreeItem _OldItem, FormTreeItem _NewItem, FormTreeSelect _how) { super(_OldItem, _NewItem, _how); if (treeDatasource) { treeDatasource.selectionChanged(_OldItem, _NewItem); } } } }






One thought on “AX – D365FO – Create a Simple and Detail – Tree Form”