Working with Forms in Dynamics AX: Part 1

Exclusive offer: get 50% off this eBook here
Microsoft Dynamics AX 2009 Development Cookbook

Microsoft Dynamics AX 2009 Development Cookbook — Save 50%

Solve real-world Microsoft Dynamics AX development problems with over 60 simple but incredibly effective recipes with this book and eBook

$29.99    $15.00
by Mindaugas Pocius | December 2009 | .NET Cookbooks Microsoft

Forms in Dynamics AX represent the user interface and are mainly used for entering or modifying data. They are also used for running reports, executing user commands, validating data, and so on.

In this three-part article by Mindaugas Pocius, we will cover:

  • Creating dialogs
  • Handling dialog events
  • Creating dynamic menu buttons
  • Building dynamic form
  • Adding form splitters
  • Creating modal form
  • Changing common form appearance
  • Storing last form values
  • Using tree controls
  • Building checklists
  • Adding a "Go to the Main Table Form" link
  • Modifying the User setup form
  • Modifying application version

Introduction

Normally, forms are created using AOT by creating a form object and adding form controls like tabs, tab pages, grids, groups, data fields, images, and other. Form behavior is controlled by its properties or the code in its member methods. The behavior and layout of form controls are also controlled by their properties and the code in their member methods. Although it is very rare, forms can also be created dynamically from code.

In this article, we will cover various aspects of using Dynamics AX forms. We start with building Dynamics AX dialogs, which actually are dynamic forms, and explain how to handle their events. The article will also show how to add dynamic controls to existing forms, how to build dynamic forms from scratch, how to make forms modal, and how to change the appearance of all application forms with a few lines of code.

This article also discusses the usage of splitters, tree controls, creating checklists, saving last user selections, modifying application version, and other things.

Creating Dialogs

Dialogs are a way to present users with a simple input form. They are commonly used for small user tasks like filling in report values, running batch jobs, presenting only the most important fields to the user when creating a new record, etc. Dialogs are normally created from X++ code without storing actual layout in AOT.

The application class Dialog is used to build dialogs. Other application classes like DialogField, DialogGroup , DialogTabPage, and so on, are used to create dialog controls. One of the common ways is to use dialogs within RunBase framework classes that need user input.

In this example, we will see how to build a dialog from code using the RunBase framework class. The dialog will contain customer table fields shown in different groups and tabs for creating a new record. There will be two tab pages, General and Details. The first page will have Customer account and Name input controls. The second page will be divided into two groups, Setup and Payment, with relevant fields. The actual record will not be created, as it is out of scope of this example. However, for demonstration purposes, the information specified by the user will be displayed in the Infolog.

How to do it...

  • Open AOT, and create a new class CustCreate with the following code:
  • class CustCreate extends RunBase
    {
    DialogField fieldAccount;
    DialogField fieldName;
    DialogField fieldGroup;
    DialogField fieldCurrency;
    DialogField fieldPaymTermId;
    DialogField fieldPaymMode;
    CustAccount custAccount;
    CustName custName;
    CustGroupId custGroupId;
    CurrencyCode currencyCode;
    CustPaymTermId paymTermId;
    CustPaymMode paymMode;
    }
    public container pack()
    {
    return connull();
    }
    public boolean unpack(container packedClass)
    {
    return true;
    }
    protected Object dialog()
    {
    Dialog dialog;
    DialogTabPage tabGeneral;
    DialogTabPage tabDetails;
    DialogGroup groupCustomer;
    DialogGroup groupPayment;
    ;

    dialog = super();
    dialog.caption("Customer information");
    tabGeneral = dialog.addTabPage("General");
    fieldAccount = dialog.addField(
    typeid(CustVendAC),
    "Customer account");
    fieldName = dialog.addField(typeid(CustName));
    tabDetails = dialog.addTabPage("Details");
    groupCustomer = dialog.addGroup("Setup");
    fieldGroup = dialog.addField(typeid(CustGroupId));
    fieldCurrency = dialog.addField(typeid(CurrencyCode));
    groupPayment = dialog.addGroup("Payment");
    fieldPaymTermId = dialog.addField(typeid(CustPaymTermId));
    fieldPaymMode = dialog.addField(typeid(CustPaymMode));
    return dialog;
    }
    public boolean getFromDialog()
    {;
    custAccount = fieldAccount.value();
    custName = fieldName.value();
    custGroupId = fieldGroup.value();
    currencyCode = fieldCurrency.value();
    paymTermId = fieldPaymTermId.value();
    paymMode = fieldPaymMode.value();
    return true;
    }
    public void run()
    {;
    info("You have entered customer information:");
    info(strfmt("Account: %1", custAccount));
    info(strfmt("Name: %1", custName));
    info(strfmt("Group: %1", custGroupId));
    info(strfmt("Currency: %1", currencyCode));
    info(strfmt("Terms of payment: %1", paymTermId));
    info(strfmt("Method of payment: %1", paymMode));
    }
    static void main(Args _args)
    {
    CustCreate custCreate = new CustCreate();
    ;

    if (custCreate.prompt())
    {
    custCreate.run();
    }
    }
  • To test the dialog, run the class. The following form should appear with the General tab page open initially:
  • Microsoft Dynamics AX 2009 Development Cookbook

  • When you click on the Details tab page, you will see the following screen:
  • Microsoft Dynamics AX 2009 Development Cookbook

  • Enter some information into the fields, and click OK. The results are displayed in the Infolog:
  • Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    Firstly, we create a new class CustCreate. By extending it from RunBase, we utilize the standard approach of running such kinds of dialogs. RunBase will also automatically add the required buttons to the dialog. Then we declare class member variables, which will be used later. DialogField type variables are actual user input fields. The rest are used to store the values returned from user input.

    The pack() and unpack() methods are normally used to convert an object to a container, which is a format to store an object in the user cache (SysLastValue) or to transfer it between Server and Client tiers. RunBase requires those two methods to be present in all its subclasses. In this example, we are not using any of the pack()/unpack() features, but because those methods are mandatory, we return an empty container from pack() and true from unpack().

    The layout of the actual dialog is constructed in the dialog() member method. Here, we define local variables for the dialog itself, tab pages, and groups. Those variables, as opposed to the dialog fields, do not store any value to be processed further.

    The super() of the RunBase framework creates the initial dialog object for us. The object is created using the Dialog application class. The class actually uses the Dynamics AX form as a base, automatically adds the relevant controls, including OK and Cancel buttons, and presents it to the user as a dialog.

    Additional dialog controls are added to the dialog by using the addField(), addGroup(), and addTabPage() methods . There are more methods to add different types of controls like addText(), addImage(), addMenuItemButton(), and others. All controls have to be added to the dialog object directly. Adding an input control to groups or tabs is done by calling addField() right after addGroup() or addTabPage(). In the example above, we add tab pages, groups, and fields in logical sequence, so every control appears in the right position.

    The method returns a prepared dialog object for further processing.

    Values from the dialog controls are assigned to variables by calling the value() member method of DialogField. If a dialog is used within the RunBase framework, as in this example, the best place to assign dialog control values to variables is the getFormDialog() member method. RunBase calls this method right after the user clicks OK.

    The main processing is done in run(). For demonstration purposes, this example contains only variable output to Infolog.

    In order to make this class runable, the static method main() has to be created. Here, we create a new CustCreate object, invoke user dialog by calling prompt(), and once the user finishes entering customer details by clicking OK, we call run() to process the data.

    Handling dialog events

    Sometimes, the user interface requires us to change the status of a field, depending on the status of another field. For example, if the user marks the Show filter checkbox, another field, Filter, appears or becomes enabled. In standard Dynamics AX forms, this can be done using input control event modified() . But sometimes such features are required on dialogs where handling events is not that straightforward.

    Very often, I find myself in a situation where existing dialogs need to be adjusted to support events. The easiest way of doing that is of course to build a form in AOT, which will replace the original dialog. But in cases when the existing dialog is complex enough, probably a more cost effective solution would be to implement dialog event handling. It is not as flexible as AOT forms, but in most cases it does the job.

    In this recipe, we will create a dialog very similar to the previous one, but instead of entering the customer number, we will be able to select it from the list. Once the customer is selected, the rest of the fields will be filled automatically by the system from the customer record.

    How to do it...

    1. In AOT, create a new class named CustSelect with the following code:
        class CustSelect extends RunBase
        {
        DialogField fieldAccount;
        DialogField fieldName;
        DialogField fieldGroup;
        DialogField fieldCurrency;
        DialogField fieldPaymTermId;
        DialogField fieldPaymMode;
        }
        public container pack()
        {
        return connull();
        }
        public boolean unpack(container packedClass)
        {
        return true;
        }
        protected Object dialog()
        {
        Dialog dialog;
        DialogTabPage tabGeneral;
        DialogTabPage tabDetails;
        DialogGroup groupCustomer;
        DialogGroup groupPayment;
        ;

        dialog = super();
        dialog.caption("Customer information");
        dialog.allowUpdateOnSelectCtrl(true);
        tabGeneral = dialog.addTabPage("General");
        fieldAccount = dialog.addField(
        typeid(CustAccount),
        "Customer account");
        fieldName = dialog.addField(typeid(CustName));
        fieldName.enabled(false);
        tabDetails = dialog.addTabPage("Details");
        groupCustomer = dialog.addGroup("Setup");
        fieldGroup = dialog.addField(typeid(CustGroupId));
        fieldCurrency = dialog.addField(typeid(CurrencyCode));
        fieldGroup.enabled(false);
        fieldCurrency.enabled(false);
        groupPayment = dialog.addGroup("Payment");
        fieldPaymTermId = dialog.addField(typeid(CustPaymTermId));
        fieldPaymMode = dialog.addField(typeid(CustPaymMode));
        fieldPaymTermId.enabled(false);
        fieldPaymMode.enabled(false);
        return dialog;
        }
        public void dialogSelectCtrl()
        {
        CustTable custTable;
        ;

        custTable = CustTable::find(fieldAccount.value());
        fieldName.value(custTable.Name);
        fieldGroup.value(custTable.CustGroup);
        fieldCurrency.value(custTable.Currency);
        fieldPaymTermId.value(custTable.PaymTermId);
        fieldPaymMode.value(custTable.PaymMode);
        }
        static void main(Args _args)
        {
        CustSelect custSelect = new CustSelect();
        ;

        if (CustSelect.prompt())
        {
        CustSelect.run();
        }
        }

    2. Run the class, select any customer from the list, and move the cursor to the next control. Notice how the rest of the fields were populated automatically with the customer information:
    3. Microsoft Dynamics AX 2009 Development Cookbook

    4. When you click on Details tab page, you will see the details as in following screenshot:
    5. Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    The new class CustSelect is a copy of CustCreate from the previous recipe with few changes. In its declaration, we leave all DialogField declarations. We remove all other variables apart from Customer account. The Customer account input control is the only editable field on the dialog, so we have to keep it for storing its value.

    The methods pack()/unpack() remain the same as we are not using any of their features.

    In the dialog() member method, we call allowUpdateOnSelect() with the argument true to enable input control event handling. We also disable all fields apart from Customer account by calling enable() with parameter false for every field.

    The member method dialogSelectCtrl() of the RunBase class is called every time the user modifies any input control in the dialog. It is the place where we have to add all the required code to ensure that, in our case, all controls are populated with the correct data from the customer record, once the Customer account is chosen.

    Static main() method ensures that we can run this class.

    There's more...

    Usage of dialogSelectCtrl() sometimes might appear a bit limited as this method is only invoked when the dialog control loses its focus. No other events can be controlled, and it can become messy if more controls needs to be processed. Actually, this method is called from the selectControl() of the form, which is used as a base for the dialog.

    As mentioned earlier, dialogs created using the Dialog class are actually forms, which are dynamically created during runtime. So in order to extend event handling functionality on dialogs, we should utilize form event handling features.

    The Dialog class does not provide direct access to form event handling functions, but we can easily access the form object within the dialog. Although we cannot create the usual event handling methods on runtime form controls, we can override this behavior. Let's modify the previous example to include more events. We will add an event on the second tab page, which is triggered once the page is activated. First, we have to override the dialogPostRun() method on the CustSelect class:

    public void dialogPostRun(DialogRunbase dialog)
    {;
    dialog.formRun().controlMethodOverload(true);
    dialog.formRun().controlMethodOverloadObject(this);
    super(dialog);
    }

    Here, we enable event overriding on the form after it is fully created and is ready for displaying on the screen. We also pass the CustSelect object as argument for the controlMethodOverloadObject() to make sure that form "knows" where overridden events are located.

    Next, we have to create the method that overrides the tab page event:

    void TabPg_2_pageActivated()
    {;
    info('Tab page activated');
    }

    The method name consists of the control name and event name joined with an underscore. But before creating such methods, we first have to get the name of the runtime control. This is because the dialog form is created dynamically, and Dynamics AX defines control names automatically without allowing the user to choose them. In this example, I have temporary added the following code to the bottom of dialog(), which displayed the name of the Details tab page control when the class was executed:

    info(tabDetails.name());

    Now, run the class again, and select the Details tab page. The message should be displayed in the Infolog.

    Creating dynamic menu buttons

    Normally, Dynamics AX forms are created in AOT by adding various controls to the form's design and do not change during runtime. But besides that, Dynamics AX allows developers to add controls dynamically during form runtime.

    Probably, you have already noticed that the Document handling form in the standard Dynamics AX application has a nice option to create a new record by clicking the New button and selecting the desired document type from the list. This feature does not add any new functionality to the application, but it provides an alternative way of quickly creating a new record and it makes the form more user-friendly. The content of this button is actually generated dynamically during the initialization of the form and may vary depending on the document handling setup.

    There might be other cases when such features can be used. For example, dynamic menu buttons could be used to display a list of statuses, which depends on the type of the selected record.

    In this recipe, we will explore the code behind this feature. As an example, we will modify the Ledger budget button on the Chart of accounts form to display a list of available budget models relevant only for the selected ledger account. That means the list is going to be generated dynamically and may be different for different accounts.

    Microsoft Dynamics AX 2009 Development Cookbook Solve real-world Microsoft Dynamics AX development problems with over 60 simple but incredibly effective recipes with this book and eBook
    Published: December 2009
    eBook Price: $29.99
    Book Price: $49.99
    See more
    Select your format and quantity:

    How to do it...

  • Open the LedgerTable form in AOT.
  • Add the following variable to its class declaration:

  • Map modelMap;

  • Delete the ButtonBudget menu item button located in the form's ButtonGroup.
  • Create a new button of type MenuButton instead in the same place as ButtonBudget with the following properties:
  • Property

    Value

    Name

    ButtonBudget

    AutoDeclaration

    Yes

    Text

    Ledger budget

  • Override its clicked() method:

  • void clicked()
    {
    MapIterator mapIterator;
    BudgetModel model;
    LedgerBudget budget;
    FormFunctionButtonControl menuItemButton;
    ;

    if (modelMap)
    {
    mapIterator = new MapIterator(modelMap);
    mapIterator.begin();
    while (mapIterator.more())
    {
    element.design().removeControl(mapIterator.key());
    mapIterator.next();
    }
    }
    modelMap = new Map(Types::Integer, Types::String);
    while select model
    where model.Type == HeadingSub::Heading
    && model.Blocked == NoYes::No
    exists join budget
    where budget.ModelNum == model.ModelId
    && budget.AccountNum == LedgerTable.AccountNum
    {
    menuItemButton = ButtonBudget.addControl(
    FormControlType::MenuFunctionButton,
    'LedgerBudgetModel');
    menuItemButton.text(model.Txt);
    modelMap.insert(menuItemButton.id(), model.ModelId);
    }
    super();
    }

  • Add a new MenuItemButton to this MenuButton with properties:
  • Property

    Value

    Name

    LedgerBudget

    MenuItemType

    Display

    MenuItemName

    LedgerBudget

    Text

    All models

  • Insert a Separator afterwards.
  • The form should look like this in AOT:
  • Microsoft Dynamics AX 2009 Development Cookbook

  • Next, add the following code to the top of the form's run() right after the variable declaration section:
  • element.controlMethodOverload(true);
  • And finally, create a new form method:
  • public void ledgerBudgetModel_clicked()
    {
    FormFunctionButtonControl menuItemButton;
    BudgetModelId modelId;
    Args args;
    FormRun formRun;
    FormDataSource fds;
    QueryBuildDataSource qbds;
    QueryBuildRange qbr;
    ;

    menuItemButton = element.controlCallingMethod();
    modelId = modelMap.lookup(menuItemButton.id());
    args = new Args(formstr(LedgerBudget));
    args.record(LedgerTable);
    formRun = classfactory.formRunClass(args);
    formRun.init();
    fds = formRun.dataSource();
    qbds = fds.query().dataSourceTable(
    tablenum(LedgerBudget));
    qbr = qbds.addRange(fieldnum(LedgerBudget,ModelNum));
    qbr.value(queryvalue(modelId));
    qbr.status(RangeStatus::Hidden);
    formRun.run();
    formRun.wait();
    }
  • To test the results, open General ledger | Chart of Account Details, and expand the Ledger budget menu button for several accounts containing budgets for different models. Notice how the content of the Ledger budget menu changes depending on what budgets are configured for the selected account:
  •  

    Microsoft Dynamics AX 2009 Development Cookbook

     

     

    Microsoft Dynamics AX 2009 Development Cookbook

     

  • If you click on one of the available options, for example, Total Model for account 110110, then you get a list of ledger budget records for this particular account and this particular model:
  • How it works...

    We start the example by adding a new Map variable to the form's class declaration section. We will use it temporarily for storing a list of available budget models along with dynamically created control numbers.

    The next step is to replace the existing Ledger budget menu item button with a menu button. This will allow a user to expand it and select one of the options inside it. Notice that the new button is named exactly the same as the previous one. This is because there is a standard code on the form that handles the button's availability depending on the type of selected account. By leaving the same name, we ensure that the code will compile and work properly.

    Now, we need to override the button's clicked(). The code in here will be executed once the user has clicked on the Ledger budget button, and it will create dynamic menu item buttons. The code has to be before super() to make sure the super() "knows" about all the controls in this menu button.

    The code starts with first removing all existing items, in case they were created previously. We have to perform this operation, otherwise the list will keep increasing with every user click. We loop though the modelMap variable and remove every control from the form's design that matches the value in the Map. This part of the code is not executed when a user opens the Ledger budget menu button for the first time, because modelMap is not initialized yet. After this, we reset the Map by recreating it.

    The second part of this method loops through existing budget models for the currently selected ledger account and creates a new menu item button for every model. Here, we also prepare the modelMap variable for further use by storing the number of the newly created control and the budget model ID. Notice that we have set the same name LedgerBudgetModel for every control created. As we will see later, this is done on purpose.

    To finish with the Ledger budget menu button, we also add a menu item button pointing to the original Ledger budget form along with a separator. There are two reasons for this. First, we still want to keep the original functionality allowing the user to open the Ledger budget form if there are no budget records. And secondly, by having something inside the Ledger budget menu button, we force it to be displayed during the initialization of the form. If this menu button was empty, it would simply not be displayed on the form and the code in its clicked() method would never be executed. Lastly, the separator simply distinguishes the static and dynamic parts of the content.

    Finally, we have to ensure that the correct form is opened once the user selects one of the dynamic options. We could have assigned the LedgerBudget menu item to every control created, but this would not filter the opened Ledger budget form with the chosen budget model. In order to achieve that, we have to override the button's event with our code. By calling controlMethodOverload(true), we enable form control event overriding. Now, we have to create a new form method with a name consisting of the control name and event name separated by an underscore, i.e. for our dynamic control named LedgerBudgetModel, we have to create ledgerBudgetModel_clicked(). In this method, we retrieve the value of the currently selected budget model, create and initialize the LedgerBudget form, apply the budget model filter to its data source, and run it. In this way, we simulate the execution of the LedgerBudget menu item and also set the required filter.

    The last thing to mention here is about the security. When building dynamic controls, it is very important that the created functionality can still be properly controlled by the Dynamics AX security system. For example, in this recipe, if a user does not have access to the LedgerBudget menu item, then the Ledger budget menu button will be automatically inaccessible, and there will be no further access to dynamic buttons inside it. This is because during form initialization, only "real" controls are considered so as only the menu button control's LedgerBudget menu item is disabled by the security, the whole menu button will be hidden too.

    Building dynamic form

    A standard approach for creating forms in Dynamics AX is to create and store form objects in AOT. This ensures system performance and a high level of complexity. But in a number of cases, it is required to have forms created dynamically. Looking at the standard Dynamics AX application, we can see that application objects like the Table browser form, various lookups, or dialogs are built dynamically.

    In this recipe, we will create a dynamic form. To show how flexible it can be, we will replicate the layout of the existing transaction type form in the Bank module. It can be opened from Bank | Setup | Bank transaction type.

    How to do it...

  • In AOT, create a new class called BankTransTypeDynamic with the following code:
  • static void main(Args _args)
    {
    DictTable dictTable;
    Form form;
    FormBuildDesign design;
    FormBuildDataSource ds;
    FormBuildTabControl tab;
    FormBuildTabPageControl tp1;
    FormBuildTabPageControl tp2;
    FormBuildGridControl grid;
    FormBuildGroupControl grp1;
    FormBuildGroupControl grp2;
    FormBuildGroupControl grp3;
    Args args;
    FormRun formRun;
    ;

    dictTable = new DictTable(tablenum(BankTransType));
    form = new Form();
    form.name("BankTransTypeDynamic");
    ds = form.addDataSource(dictTable.name());
    ds.table(dictTable.id());
    design = form.addDesign('design');
    design.caption("Bank transaction type");
    tab = design.addControl(FormControlType::Tab, "Tab");
    tab.widthMode(FormWidth::ColumnWidth);
    tab.heightMode(FormHeight::ColumnHeight);
    tp1 = tab.addControl(FormControlType::TabPage, "Tp1");
    tp1.caption("Overview");
    tp2 = tab.addControl(FormControlType::TabPage, "Tp2");
    tp2.caption("General");
    grid = tp1.addControl(FormControlType::Grid, "Grid");
    grid.dataSource(ds.name());
    grid.widthMode(FormWidth::ColumnWidth);
    grid.heightMode(FormHeight::ColumnHeight);
    grid.addDataField(
    ds.id(),
    fieldnum(BankTransType, BankTransType));
    grid.addDataField(
    ds.id(),
    fieldnum(BankTransType, Name));
    grid.addDataField(
    ds.id(),
    fieldnum(BankTransType, LedgerAccountNum));
    grp1 = tp2.addControl(FormControlType::Group, "Grp1");
    grp1.dataSource(ds.id());
    grp1.autoDataGroup(true);
    grp1.dataGroup(
    tablefieldgroupstr(BankTransType, Identification));
    grp2 = tp2.addControl(FormControlType::Group, "Grp2");
    grp2.dataSource(ds.id());
    grp2.autoDataGroup(true);
    grp2.dataGroup(tablefieldgroupstr(BankTransType, Name));
    grp3 = tp2.addControl(FormControlType::Group, "Grp3");
    grp3.dataSource(ds.id());
    grp3.autoDataGroup(true);
    grp3.dataGroup(tablefieldgroupstr(BankTransType, Ledger));
    args = new Args();
    args.object(form);
    formRun = classfactory.formRunClass(args);
    formRun.init();
    formRun.run();
    formRun.detach();
    }
  • To test the form, run the class. Notice that the form is very much similar to the one in Bank | Setup | Bank transaction type:
  • Microsoft Dynamics AX 2009 Development Cookbook

  • Click on the General tab page to display additional information:
  • Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    We start the code with declaring variables. Note that most of them begin with FormBuild, which is a set of application classes used for building dynamic controls. Each of those types corresponds to the control types manually used when building forms in AOT.

    Right after the variable declaration, we create a dictTable object based on the BankTransType table. We will reuse this object later a number of times.

    Next, we create a form object and assign a name by calling:

    form = new Form();
    form.name("BankTransTypeDynamic");

    The form should have a data source, so we add one by calling addDataSource() on the form object:

    ds = form.addDataSource(dictTable.name());
    ds.table(dictTable.id());

    Every form has a design; so does ours:


    design = form.addDesign('design');
    design.caption("Bank transaction type");

    Once the design is ready, we can start adding controls. First, we add a tab control by automatically expanding width and height:

    tab = design.addControl(FormControlType::Tab, "Tab");
    tab.widthMode(FormWidth::ColumnWidth);
    tab.heightMode(FormHeight::ColumnHeight);

    Then we add two tab pages, Overview and General:


    tp1 = tab.addControl(FormControlType::TabPage, "Tp1");
    tp1.caption("Overview");

    tp2 = tab.addControl(FormControlType::TabPage, "Tp2");
    tp2.caption("General");

    The first tab page contains a grid pointing to the data source and automatically expanding height and width:

    grid = tp1.addControl(FormControlType::Grid, "Grid");
    grid.dataSource(ds.name());
    grid.widthMode(FormWidth::ColumnWidth);
    grid.heightMode(FormHeight::ColumnHeight);

    We add three controls pointing to relevant table fi elds by calling addDataField() on grid:

    grid.addDataField(
    ds.id(),
    fieldnum(BankTransType, BankTransType));
    grid.addDataField(
    ds.id(),
    fieldnum(BankTransType, Name));
    grid.addDataField(
    ds.id(),
    fieldnum(BankTransType, LedgerAccountNum));

    The second tab page contains three groups, which we add by addControl() with the first argument FormControlType::Group. Here, we save some time and code by making those groups automatic by calling autoDataGroup() with true:

    Group 1:

    grp1 = tp2.addControl(FormControlType::Group, "Grp1");
    grp1.dataSource(ds.id());
    grp1.autoDataGroup(true);
    grp1.dataGroup(
    tablefieldgroupstr(BankTransType, Identification));

    Group 2:

    grp2 = tp2.addControl(FormControlType::Group, "Grp2");
    grp2.dataSource(ds.id());
    grp2.autoDataGroup(true);
    grp2.dataGroup(tablefieldgroupstr(BankTransType, Name));

    Group 3:

    grp3 = tp2.addControl(FormControlType::Group, "Grp3");
    grp3.dataSource(ds.id());
    grp3.autoDataGroup(true);
    grp3.dataGroup(tablefieldgroupstr(BankTransType, Ledger));

    The last lines of code initialize and run the form.

    >> Continue Reading Working with Forms in Dynamics AX: Part 2

     

    [ 1 | 2 | 3]

     

    If you have read this article you may be interested to view :

    Microsoft Dynamics AX 2009 Development Cookbook Solve real-world Microsoft Dynamics AX development problems with over 60 simple but incredibly effective recipes with this book and eBook
    Published: December 2009
    eBook Price: $29.99
    Book Price: $49.99
    See more
    Select your format and quantity:

    About the Author :


    Mindaugas Pocius

    Mindaugas Pocius is currently a freelance Dynamics AX technical and functional consultant and trainer at DynamicsLab Limited (http://www.dynamicslab.com). The company specializes in providing development, consulting, and training services for Microsoft Dynamics AX resellers and customers.

    Mindaugas started his IT consultant career back in 2001 while still in his Information Technology Master Studies at a Lithuanian University. Since then he has become a recognized Microsoft Certified Professional for Dynamics AX in most of the major areas: Development, Configuration & Installation, Financials, Trade & Logistics and Project Management. He is also a Certified Microsoft Trainer for Dynamics AX and has delivered Dynamics AX Development, Finance and other training across the world. From 2001 to 2012, Mindaugas participated in numerous Dynamics AX implementations, ranging from small local to large international projects reaching an 800 strong user base. He has held a wide range of development, consulting, and leading roles while always maintaining a significant role as a business application developer.

    Books From Packt

    Ext JS 3.0 Cookbook
    Ext JS 3.0 Cookbook

    Joomla! 1.5 Development Cookbook
    Joomla! 1.5 Development Cookbook

    WordPress 2.7 Cookbook
    WordPress 2.7 Cookbook

    WordPress 2.8 Themes Cookbook
    WordPress 2.8 Themes Cookbook

    Microsoft Dynamics AX 2009 Programming: Getting Started
    Microsoft Dynamics AX 2009 Programming: Getting Started

    Programming Microsoft Dynamics NAV 2009
    Programming Microsoft Dynamics NAV 2009

    Microsoft Office Live Small Business: Beginner’s Guide
    Microsoft Office Live Small Business: Beginner’s Guide

    Quality Assurance for Dynamics AX-Based ERP Solutions
    Quality Assurance for Dynamics AX-Based ERP Solutions

    Your rating: None Average: 5 (1 vote)

    Post new comment

    CAPTCHA
    This question is for testing whether you are a human visitor and to prevent automated spam submissions.
    i
    p
    7
    P
    3
    G
    Enter the code without spaces and pay attention to upper/lower case.
    Code Download and Errata
    Packt Anytime, Anywhere
    Register Books
    Print Upgrades
    eBook Downloads
    Video Support
    Contact Us
    Awards Voting Nominations Previous Winners
    Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
    Resources
    Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software