Runtime form controls

Creating runtime form controls can be useful in a variety of circumstances. Any type of control can be added at runtime: groups, string edits, buttons etc.

Standard Axapta uses this technique to dynamically add 'Action' buttons to forms when required. The code to add controls for these functions is called from the new method of SysSetupFormRun.

Introduction
The basic process to add controls and intercept their events at runtime is as follows:
 * use form.addControl(ControlType, ControlName) to add the control
 * call form.controlMethodOverload(true) to enable over-riding of the control events
 * write appropriate event handler methods on the form

If you are dealing only with buttons, then it is possible to set the MenuItemType and MenuItemName on the button when it is created. In this way, you can directly call a report, class or another form, and you don't need to write additional code on the form to deal with the button click.

The Basics
The simplest example uses the init method of a form to add the controls, and design-time methods to intercept the control events. Take note of the control names used during the .addControl call. In the example below, the control names are 'DynamicStringControl' and 'DynamicButtonControl'.

public void init {    FormStringControl   formStringControl; FormButtonControl  formButtonControl; FormGroupControl   formGroupControl; ;    // Adding a group formGroupControl = this.form.addControl(FormControlType::Group, "MyGroup"); formGroupControl.caption("It's my group!"); // Adding a string to a group formStringControl = formGroupControl.addControl(FormControlType::String, "DynamicStringControl"); formStringControl.label("Dynamic string control"); // Adding another string to the group using the same name. This will use the same event method as the // first "DynamicStringControl" formStringControl = formGroupControl.addControl(FormControlType::String, "DynamicStringControl"); formStringControl.label("Dynamic string control2"); formButtonControl = this.form.addControl(FormControlType::Button, "DynamicButtonControl"); formButtonControl.text("Dynamic button"); this.controlMethodOverload(true); super; }

You can then write event handler methods for the controls at the top (element) level of your form.

void dynamicButtonControl_Clicked {    ;     info ("Dynamic button clicked!"); }

boolean dynamicStringControl_Modified {    FormStringControl control = element.controlCallingMethod; ;    info (strFmt("Modified dynamic control %1 to %2", int2str(control.id), control.text)); return true; } Note that the first part of the handler method names (before the underscore) exactly matches the control names used in init.

Adding menu-item buttons
It is usually preferable to keep the amount of code on a form to a minimum. By setting your dynamically-created buttons to call menu-items, you can avoid writing the event handlers completely.

The following example adds a menu-item button to a form, which will open the SalesTable form using the standard Axapta menu-item. Note that we now must use a FormFunctionButtonControl, rather than a FormButtonControl.

The button will correctly show the label from the menu-item.

public void init {    FormFunctionButtonControl   formFunctionButtonControl; ;    formFunctionButtonControl = this.form.addControl(FormControlType::MenuFunctionButton, "SalesTableButton"); formFunctionButtonControl.menuItemType(MenuItemType::Display); formFunctionButtonControl.menuItemName(MenuItemDisplayStr(SalesTable)); super; }

The same principle can be used to call your own 'display', 'output' or 'action' menu-item. This is useful if the button should perform some intensive database processing, which would benefit from being encapsulated in a server-side class.

See the create method on the KMActionMenuButtonAuto class for a further, more complex example.

See [[Media:AJ_DynamicControls.xpo|this project]] for two forms demonstrating the above concepts.

Adding grid control
In most cases you'd better use visible property of this control's type. But sometimes you have to add a grid on an existing form and without any opportunity of change. This example will help you. Rather you are making this modification on forms, that are managed from class. In our tutorial case this is runable tutorial_AddRunTimeControls class. This following code adds a grid control of the selected table. You may find similar functionality in the form SysTableBrowser:

void addRunTimeControls(FormRun _formRun) {    FormGridControl        formGrigControl; Form                  _form; FormBuildDataSource   formBuildDataSource; FormBuildDesign       formBuildDesign; FormBuildStringControl formBuildStringControl; FormBuildGridControl  formBuildGridControl; Object                formBuildControl; FormBuildGroupControl formGroupControl; CustTable             custTable; DictTable             dictTable; TableId               tableID; ;

// Adding a Grid control, this used in  SysTableBrowser tableID   = picktable; dictTable  = new DictTable(tableID); _form = _formRun.form; formBuildDataSource = _form.dataSource(2); formBuildDataSource.name(dictTable.name); formBuildDataSource.table(dictTable.id); formBuildDataSource.autoQuery(true); formBuildDesign = _form.design; formBuildDesign.widthMode(-1); formBuildDesign.widthValue(600); formBuildDesign.heightMode(-1); formBuildDesign.heightValue(400);

formGroupControl = formBuildDesign.addControl(FormControlType::Group, 'SecondGrid'); formGroupControl.caption("Dynamic Grid"); formGroupControl.widthMode(1); formGroupControl.heightMode(1); formBuildGridControl = formGroupControl.addControl(FormControlType::GRID,'Grid'); formBuildGridControl.dataSource(dictTable.name); formBuildGridControl.name('AddRunTime');

formBuildGridControl.widthMode(1); formBuildGridControl.heightMode(1); this.showFields(tableNum(custTable), formBuildGridControl, formBuildDataSource);

_formRun.controlMethodOverload(true); _formRun.init; _formRun.run; _formRun.detach; }

And define the fields on grid.

public void showFields(tableId                tableId,                       FormBuildGridControl    formBuildGridControl,                       FormBuildDataSource     formBuildDataSource)

{   FormBuildStringControl          formBuildStringControl; DictTable                      dictTable; fieldId                        fieldId; int                            i;    int                             fieldCnt; ;   dictTable   = new DictTable(tableId); for (i=1; i<=3; i++) {       fieldId = dictTable.fieldCnt2Id(i); formBuildGridControl.addDataField(formBuildDataSource.id, fieldId2Ext(fieldId, 1)); } } See [[Media:Axaptapedia.xpo|this project]] for this example.

Removing the control
To remove a control again, use the following code:

element.design.removeControl(elementId)

This also can be usefull to see how a dialog field properties can be changed on the fly