Runtime form controls
From Axaptapedia
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.
Contents |
[edit] 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.
[edit] 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().
[edit] 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 this project for two forms demonstrating the above concepts.
[edit] 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 this project for this example.