D365 Finance & Operation

Multi select lookup using a Tree in Microsoft Dynamics 365 F&O

Requirement:

To have multi-selection lookup of product category in a tree view on form control in Microsoft Dynamics 365 finance and operation.

Solution:

Developing a custom form to control the multi selection on the tree view and calling it on the form control lookup method.

Creating new multi selection tree view form “EcoResCategoryMultiLookup” have datasource “EcoResCategoryHierarchy” and “EcoResCategory” table . Add Tree control in the form with the following properties , in my case I named that Tree control as ctrlFormTree. (as shown in Figure 1.1)

  1. Auto Declaration = Yes
  2. CascaseSelect = No
  3. Check Box = Yes
  4. Single Selection = No
Figure 1.1

after setting the properties, we have to assign the datasource and the root element of the tree view by using class “EcoResCategoryTreeDatasource”

In the run method of the form use the following way to assign the root element and the datasource of the Tree control. In my case I am using product category to be displayed so I am using EcoResCategory table for the datasource of the Tree control.

 public void run()
    {
        QueryBuildRange     range;
        EcoResCategory      root = null;

        if (isConfigurationkeyEnabled(configurationKeyNum(Retail))
            && isMultiHierarchy)
        {
            selectedCategoryHierarchy = EcoResCategoryHierarchy::findByName(HierarchySelector.valueStr());
        }

        // display tree only if hierarchy exists
        if (selectedCategoryHierarchy.RecId)
        {
            range = ecoResCategoryHierarchy_ds.query().dataSourceTable(tablenum(EcoResCategoryHierarchy))
.addRange(fieldnum(EcoResCategoryHierarchy, RecId));
            range.value( queryValue(selectedCategoryHierarchy.RecId ) );
            ecoResCategoryHierarchy_ds.executeQuery();

            root = EcoResCategory::getRoot(selectedCategoryHierarchy.RecId);

            //tree view
            hierarchyTree = new EcoResCategoryTreeDatasource(  ecoResCategory_ds,
                                                               ctrlFormTree,
                                                               fieldnum(EcoResCategory, RecId),
                                                               fieldnum(EcoResCategory, ParentCategory),
                                                               fieldnum(EcoResCategory, Name),
                                                               false /* No ID in description */,
                                                               true,
                                                               0,
                                                               selectedCategoryHierarchy,
                                                               ecoResCategoryHierarchy_ds,
                                                               lookupParameters);

            if (root.RecId)
            {
                hierarchyTree.initRoot(root.Name, root.RecId, hierarchyTree.image());
                //select any previously selected category, only if the hierarchies match.
                //and the name was not edited
                if (selectedCategory.RecId && selectedCategory.CategoryHierarchy == selectedCategoryHierarchy.RecId)// && !callingControl.hasChanged())
                {
                    NameFilter.parmFilterValue(selectedCategory.Name);
                    hierarchyTree.expandAndSelect(selectedCategory.RecId);
                }
            }
            canSelect = false;
        }
        else
        {
            info("@SYS134292");
        }

        super();

        if (selectOk)
        {
            element.selectMode(callingControl);
        }
        
    }

Note that in above run method “selectedCategoryHierarchy” is “EcoResCategoryHierarchy” table which is initialized in init method from the calling control values.

After run method we need to override the control “checkedStateChanged” method from which we will be handling the checked records and filling it in the container to mark the records. (as shown in Figure 1.1)

public void checkedStateChanged(int _Idx, FormTreeCheckedState _newState)
        {
            FormTreeItem formTreeItem ;
            FormTreeCheckedState currentState;
            int isCheckedBefore;

            super(_Idx, _newState);

            formTreeItem = ctrlFormTree.getItem(_Idx);
            
            if (formTreeItem != null)
            {
                currentState = formTreeItem.stateChecked();
                switch (currentState)
                {
                    case FormTreeCheckedState::Unchecked:
                        isCheckedBefore = conFind(selectedNodesIds,EcoResCategory::find(ctrlFormTree.getItem(_Idx).data()).RecId);
                        if(isCheckedBefore)
                        {
                            selectedNodesIds = conDel(selectedNodesIds,isCheckedBefore,isCheckedBefore);
                        }
                        break;
                    case FormTreeCheckedState::Checked:
                        selectedNodesIds += EcoResCategory::find(ctrlFormTree.getItem(_Idx).data()).RecId;
                        break;
                    case FormTreeCheckedState::Partial:
                        // TO-DO create Label
                        info("select the nodes individually");
                        break;
                    default:
                        // Do Nothing
                        break;
                }
            }

            
        }

once the checking and unchecking is done on the tree view we have to mark the formdatasource in order to retrieve it on the lookup control.

To achieve the marked records from the container we will be having one command button named as OK and on its clicked event we will be having the following code.

 public void clicked()
        {
            int             i;
            Common          selectedCategoriesHierchy;
           
            for (i = 1; i <= conLen(selectedNodesIds); i++)
            {
                selectedCategoriesHierchy = EcoResCategory::find(conPeek(selectedNodesIds, i));
                EcoResCategory_ds.findRecord(selectedCategoriesHierchy);
                EcoResCategory_ds.mark(true);
            }

            super();

        }

Remember to override the closeSelect() method of the form and to comment the super call of the method in order to return the selected values of the datesource.

one the above process is done now we have to call the above form in other form’s control lookup method.

The process is same to override the string edit control lookup method and call the custom form as below,

public void lookup()
        {
            FormDataSource formDS;
            FormRun formRun;
            Args args = new Args();
            args.name(formStr(EcoResCategoryMultiLookup));
            args.caller(this);
            args.record(lookupCategoryHierarchy);
            args.lookupField(fieldNum(EcoResCategory, RecId));
            args.parmObject(lookupParameters);

            formRun = classfactory.formRunClass(args);
            
            formRun.init();
            formDS = formRun.dataSource(2);
            this.performFormLookup(formRun);

            formRun.wait();
            if (formRun.closedOk())
            {
                 //method to get the selected values of the tree view and display on this control
                 //selectedStr is the container having names of the selected records
                //selectedId is the conatainer having recIds the selected records
                element.getSelected(formDS);
                this.text(SysOperationHelper::convertMultiSelectedValueString(selectedStr));
                selectedCategory1 = selectedId;
            }
        }

Below is the getSelected method.

public void getSelected(FormDataSource _formDS)
    {
        EcoResCategory selectedEcoResCategory;
        Common         common;
        int            recordsMarked = 0;
        
        selectedId = connull();
        selectedStr = connull();

        for (common = getFirstSelection(_formDS); common; common = _formDS.getNext())
        {
            recordsMarked++;
        }

        if (recordsMarked > 0)
        {
            for (common = getFirstSelection(_formDS); common; common = _formDS.getNext())
            {
                if (Common.TableId == tableNum(EcoResCategory))
                {
                    selectedEcoResCategory = EcoResCategory::find(Common.RecId);

                    selectedId += selectedEcoResCategory.RecId;

                    selectedStr += selectedEcoResCategory.Name;
                }
            }
        }
    }

Output:

Multi select Tree lookup
selected values view

Thanks,

Happy Daxing with Rizz 😉

4 thoughts on “Multi select lookup using a Tree in Microsoft Dynamics 365 F&O

  1. Hi Rizwan,
    We try your code but not working for us can you please provide project if you have. We are getting lookup but selection not working.

    Like

Leave a reply to Rizz Khan Cancel reply