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’.

/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */
.code .imp {font-weight: bold; color: red;}
.code .kw1 {color: #0000ff;}
.code .co1 {color: #007F00;}
.code .coMULTI {color: #007F00;}
.code .es0 {color: #666666; font-weight: bold;}
.code .br1 {color: #0000ff;}
.code .br2 {color: #0000ff;}
.code .st0 {color: #FF0000;}
.code .nu0 {color: #000000;}
.code .me1 {color: #000000;}
.code .me2 {color: #000000;}

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.

/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */
.code .imp {font-weight: bold; color: red;}
.code .kw1 {color: #0000ff;}
.code .co1 {color: #007F00;}
.code .coMULTI {color: #007F00;}
.code .es0 {color: #666666; font-weight: bold;}
.code .br1 {color: #0000ff;}
.code .br2 {color: #0000ff;}
.code .st0 {color: #FF0000;}
.code .nu0 {color: #000000;}
.code .me1 {color: #000000;}
.code .me2 {color: #000000;}

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.

/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */
.code .imp {font-weight: bold; color: red;}
.code .kw1 {color: #0000ff;}
.code .co1 {color: #007F00;}
.code .coMULTI {color: #007F00;}
.code .es0 {color: #666666; font-weight: bold;}
.code .br1 {color: #0000ff;}
.code .br2 {color: #0000ff;}
.code .st0 {color: #FF0000;}
.code .nu0 {color: #000000;}
.code .me1 {color: #000000;}
.code .me2 {color: #000000;}

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:

/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */
.code .imp {font-weight: bold; color: red;}
.code .kw1 {color: #0000ff;}
.code .co1 {color: #007F00;}
.code .coMULTI {color: #007F00;}
.code .es0 {color: #666666; font-weight: bold;}
.code .br1 {color: #0000ff;}
.code .br2 {color: #0000ff;}
.code .st0 {color: #FF0000;}
.code .nu0 {color: #000000;}
.code .me1 {color: #000000;}
.code .me2 {color: #000000;}

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.

/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */
.code .imp {font-weight: bold; color: red;}
.code .kw1 {color: #0000ff;}
.code .co1 {color: #007F00;}
.code .coMULTI {color: #007F00;}
.code .es0 {color: #666666; font-weight: bold;}
.code .br1 {color: #0000ff;}
.code .br2 {color: #0000ff;}
.code .st0 {color: #FF0000;}
.code .nu0 {color: #000000;}
.code .me1 {color: #000000;}
.code .me2 {color: #000000;}

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.

[edit] Removing the control

To remove a control again, use the following code:

/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */
.code .imp {font-weight: bold; color: red;}
.code .kw1 {color: #0000ff;}
.code .co1 {color: #007F00;}
.code .coMULTI {color: #007F00;}
.code .es0 {color: #666666; font-weight: bold;}
.code .br1 {color: #0000ff;}
.code .br2 {color: #0000ff;}
.code .st0 {color: #FF0000;}
.code .nu0 {color: #000000;}
.code .me1 {color: #000000;}
.code .me2 {color: #000000;}

element.design().removeControl(elementId)

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

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: