0

The FormButtonControl class in the Dynamics.AX.Application namespace is a kernel-level class in the Dynamics 365 for Finance and Operations application model. It extends the FormControl class and represents a button control placed on a form. As a control, it is responsible for handling interactions like click events (clicked()), focus events (gotFocus() and lostFocus()), and user interface behavior such as drag-and-drop, background style, visibility, and access control. It is commonly used by developers when they want to add an interactive button on a form that can trigger custom business logic, often via overridden methods or event subscriptions.

formbuttoncontrol in x++
formbuttoncontrol in x++

What makes FormButtonControl especially relevant in form customization is its ability to serve as an actionable control that can launch other forms or trigger processes. Through its clicked() method, developers can programmatically respond to user actions, and optionally use Args and FormRun or MenuFunction classes to open other forms from within that method. The class exposes numerous properties to define appearance (like font, color, size), behavior (like whether it’s the default button, supports drag-drop, or is editable), and access rules (like configuration keys, visibility, or user-level restrictions). It supports standard UI control operations and integrates closely with the kernel and X++ runtime, making it an essential class when building or extending forms within D365FO.

Calling a Form Using X++ in D365 Finance & Operations

In Dynamics 365 Finance & Operations (formerly AX), forms are typically opened via menu items (e.g. clicking navigation buttons). However, there are times when a developer needs to open a form programmatically – for example, from a custom button on another form. This can be done by adding a Button control and writing X++ code in its clicked event to launch the target form. There are two common approaches to call a form in X++: (1) directly instantiating and running the form by name, and (2) calling a display Menu Item associated with the form. Each approach has its use cases and considerations, especially around security and parameter passing.

Adding a Button Control to Open Another Form

To trigger form navigation in code, first add a new Button control on the source form (for example, within an existing Button Group in the form designer). In the form designer, right-click the Button Group and choose New Control > Button to create a new button. Set its properties (like label text) as desired. Next, override the button’s clicked method to add custom X++ code that will open the target form when the button is clicked. In Visual Studio (D365 FO’s development environment), you can expand the button control’s node to find Methods, then right-click clicked and select Override to insert an event handler method for the click event. The code we write in this method will instantiate and launch the desired form.

Opening a Form Programmatically (Direct FormRun Approach)

One way to open a form via X++ is by directly referencing the form name and using the FormRun class. This approach does not rely on an existing menu item. The general steps are:

  1. Create an Args object and specify the target form’s name with Args.name(). For example:
    Args args = new Args(formStr(CustTable));
    

    The formStr() macro converts a form name (like CustTable) to a form identifier. You can also attach data to args at this point (explained later). Commonly, you might pass a specific record so the form opens on that record:

    args.record(custTableRecord);  // pass a record to open form on that record
    

 

  1. Instantiate the form using the FormRun class. This is done via the classFactory utility:
    FormRun formRun = classFactory.formRunClass(args);
    

    This creates a FormRun object for the specified form using the Args provided.

  2. Initialize and run the form. After creating the FormRun, call its lifecycle methods:
    formRun.init();
    formRun.run();
    formRun.wait();
    

    The init() and run() methods set up and open the form, and wait() will halt the calling code until the new form is closed (opening the form modally). Once wait() returns, the called form has been closed. At this point, you can optionally call formRun.detach() to disconnect the form instance and then perform any follow-up actions like refreshing the caller form’s data sources (for example, calling this.datasource().research() to reflect changes made in the called form).

Below is an example of X++ code in a button’s clicked handler that opens the CustTable form via this direct approach:

void clicked()
{
    Args args = new Args(formStr(CustTable));    // specify target form
    args.record(custTableLocal);                // pass current record to open that record
    FormRun formRun = classFactory.formRunClass(args); 
    formRun.init();
    formRun.run();
    formRun.wait();
    // Optional: formRun.detach(); this.datasource().research();
}

In this example, clicking the button will open the CustTable form focused on the record held in custTableLocal (assuming that is a record from CustTable). This direct FormRun approach gives the developer full control but does not inherently enforce any security privileges (since it bypasses menu items). It’s important to ensure the user has rights to the form’s data; otherwise, the form might open but certain operations or data could be restricted. In practice, this method is best used only when you need to bypass or supplement standard navigation for a special scenario.

Using a Menu Item to Launch a Form (MenuFunction Approach)

A more common and recommended approach is to call the form via a Display Menu Item. In D365 F&O, every form intended for interactive use usually has a Display menu item defined (which is how the form is normally opened from the UI). We can leverage that in code. Instead of specifying the form name in Args, we use the menu item name and the MenuFunction class to run it. This has a key advantage: the system will automatically enforce security roles/privileges associated with that menu item. In other words, if the user doesn’t have access to that form’s menu item, the call will be blocked or fail, just as it would in the standard UI. (By contrast, a plain button with direct code has no built-in security link.)

To call a form by menu item:

  1. Create and prepare an Args object (just like before). You typically do not need to call args.name() for the form in this case, but you can attach any needed parameters (record, etc.) to args. For example:
    Args args = new Args();
    args.record(myRecord);        // optional: pass a record or other params
    args.caller(this);            // optional: identify the caller form
    

    The args.caller(this) line is not required, but it can be useful if the called form needs to know which form initiated the call (to call back or retrieve info from the caller).

  2. Instantiate a MenuFunction for the menu item and run it. Use the appropriate menu item macro (e.g. menuItemDisplayStr) with the menu item’s name, and specify the type as Display:
    MenuFunction menuFn = new MenuFunction(menuItemDisplayStr(CustTable), MenuItemType::Display);
    menuFn.run(args);
    

    This will open the form associated with the CustTable display menu item, passing in the Args. The system will check that the current user has permission to use that menu item before opening the form. (If not, the form will not open – the MenuFunction.run() essentially respects security settings.)

Here’s a concrete example of using a menu item in the clicked method. Suppose we have a button that should open a Purchase Requisition Details form for a selected requisition record:

void clicked()
{
    Args args = new Args();
    PurchReqTable purchReq = ... // (assume we retrieve the selected PurchReqTable record)
    args.record(purchReq);  // pass the selected Purchase Requisition record
    // Use the display menu item associated with PurchReqTable form:
    MenuFunction mf = new MenuFunction(menuItemDisplayStr(PurchReqTableDetails), MenuItemType::Display);
    mf.run(args);
}

In this snippet, PurchReqTableDetails is the name of a Display menu item that opens the Purchase Requisition form. We select the desired PurchReqTable record and attach it to args, so the form will open directly on that record. Because we used a MenuFunction and menu item, the security system is honored – the button effectively behaves like a menu item button. (In fact, one could also simply place a MenuItemButton control on the form pointing to the menu item, which is another no-code way to achieve this. The X++ approach is useful when additional logic is needed.)

Why use a menu item? Using a menu item is generally safer and more maintainable. Menu item controls are tied to security privileges, whereas a plain coded button is not. With a menu item approach, “it will be automatically checked whether the current user has permissions to open/run the menu item”, so you don’t risk exposing a form to someone without proper access. Additionally, menu items encapsulate the form’s link in one place – if the underlying form name changes, the menu item can be updated without breaking all the code references (code would refer to the menu item name, which likely remains consistent). For these reasons, calling via menu items is the recommended practice in most cases.

Passing Parameters with the Args Class

Whichever approach you use (FormRun or MenuFunction), the Args class is the vehicle for passing data to the new form. The called form can retrieve the passed information via its own this.args() method. Here are common ways to use Args to send parameters:

  • Record: As shown earlier, you can use args.record(<Record>) to send a table record. The called form will receive this and often use it to initialize or filter its datasource to that record. For example, passing a CustTable record into the CustTable form opens that form on the specific customer record.
  • Simple Parameters: You can use args.parm(<string>) to pass a simple string value (or data that can be stringified). This is often used for small pieces of info or tokens. The called form would call element.args().parm() to retrieve that string.
  • Enum Values: If you need to pass a Base Enum value (an option of an enum type), use the pair of parmEnumType and parmEnum methods. For example, to pass a NoYes enum indicating “Yes,” you would do:
    args.parmEnumType(enumNum(NoYes));        // specify the enum type (NoYes)
    args.parmEnum(NoYes::Yes);               // specify the enum value 
    

.
These two methods work together: you first set the enum’s type ID, then the specific value. In the called form, you could retrieve it with element.args().parmEnum(), which would return an object representing the enum value (or you can compare it directly to the enum, e.g. if (element.args().parmEnum() == NoYes::Yes) {...}).

  • Object or Complex Data: For more complex data, you can use args.parmObject(<object>). This could be a class instance, a container, or even a list of records, as long as the called form knows how to handle that object. In our earlier example using MenuFunction, we saw args.parmObject(list) being used – presumably to pass a list or container of values. The called form can retrieve it via element.args().parmObject() and cast it back to the expected type.
  • Caller Reference: Using args.caller(<FormRun>) is optional but useful when the context of the caller form is needed. For instance, if the called form might need to call back to the origin (say, refresh the original form’s data or invoke a method on it), setting args.caller(this) in the originating form’s button click will provide a reference. The new form can then do something like:
    FormRun callerForm = element.args().caller();
    

    and interact with the caller form (e.g. call a public method on it, or refresh it). This should be used judiciously to avoid tight coupling, but it’s available if needed.

Using these Args parameters, a developer has fine-grained control over what information is sent into the new form. The called form (in its init() or run() or data source methods) can read these values and adjust its behavior accordingly – for example, filtering the data source to a certain record or range, showing/hiding certain controls based on a passed flag, or even changing the form’s caption or text based on parameters. This ability to pass data is a major advantage of calling a form in code, as it enables more dynamic interactions between forms beyond what a simple menu item click might offer.

Best Practices and Conclusion

So there are pretty much two main ways to launch a form from another form via X++: directly using FormRun (by form name) or using a MenuFunction (by menu item). In most scenarios, the menu item approach is preferred because it automatically handles user security and is aligned with the standard navigation design of the application. You would typically use a MenuItem Button on a form linked to a Display Menu Item for standard form navigation needs.

However, if you have a scenario requiring more control – such as needing to pass multiple complex parameters, or opening the form in a non-standard way – then overriding a button’s clicked method and using the X++ code approach gives you that flexibility. Just remember to still respect security (ensure the user has appropriate privileges for what you’re doing) and maintainability (document or encapsulate the logic so it’s clear what form is being called). Often, you might combine approaches: for example, create a hidden menu item for a form (so it has security tied to it) but still call it via X++ to pass custom Args. This way you get the benefits of both.

By following this guidance you can  try to successfully add a button to one form and have it open another form using X++, either directly or via a menu item. With the Args class, you can send any needed information to the new form, providing a powerful way to create interactive, context-driven experiences in Dynamics 365 F&O. Always consider the user experience and security implications, and choose the approach that best fits the requirement.

Have a Question ?

Fill out this short form, one of our Experts will contact you soon.

Call Us Today For Your Free Consultation