In the previous article I showed you how to call another from using a Menu Item Button. There are times, however, when you need to call a form using x++ code. Calling a form this way gives a developer greater control around what information is passed to the calling form. And a developer can even add code that runs after the calling form is closed.
In most scenarios it is preferred to use a Menu Item Button to call a form from another form. However, in this case, I will show you how to call a form using x++. I will start with showing you how to do this. And then follow up with explaining how this can be helpful.
Setting Up The Project
In Visual Studio, create a new Finance and Operations project. Go to File>New>Project.
Select Finance Operations as the project type. (This screen will vary depending on what version of Visual Studio you are using). In the below screenshots, I am using Visual Studio 2019.
Enter in a Project name, and then click the Create button.
Right click on the project, and select Properties.
Set the ‘Model‘ to the model you are using. In my case, I set it to ‘rsmTutorials‘ model.
And, if you plan on making changes that affect the database, set the ‘Synchronize Database on Build‘ property to ‘True’.
Now, either create a form that you wish to add a button to. Or add an existing form to the project. Just like the last article, I will use my form named rsmModel. I will add a button that will call the rsmMake form.
If you do not have an Action Pane, and inside it, a Button Group control, add those to your form. My form looks like this.
Add A Button Control
Again, typically we would add a Menu Item Button to call another form. However, if we are going to use X++ code to call another form, we should instead add a Button control.
To do this, right click on the Button Group control, and select New>Button.
After the Button control is created, select it in the form designer. Then right click and select ‘Properties‘.
In the Properties window, set the ‘Name‘ property. I named mined ‘FormButtonControlMake‘.
Also in the properties window, set the ‘Text‘ property. I entered ‘Call form using x++‘. (Normally I wouldn’t do this. But I am doing so now to make it show really clear which button this is). In the Preview pane, you can see the new button and text.
Override the ‘clicked’ method.
Next, in order to call a form using x++ code, we need to override the clicked method on the button control.
Expand the Button control, until you see the ‘Methods‘ node.
Right click on the ‘Methods’ node, and select Override>clicked.
Note: If the form you are trying to add a button to does not exist in the current model, you will need to Extend the form to add the button. And then you will need to use Chain Of Command to override the clicked method on the button control.
A code editor window will open. You can always re-open this windows, by right clicking on the form, and selecting ‘View code’.
Initially, the system will generate the clicked method, and it will look like this.
Instantiate and Call FormRun Class
Now that we have added the button to the form and we have overridden the clicked method, we need to add code to call a form using x++ code.
In order to call another form, we need to instantiate an instance of the FormRun class. The FormRun class is the base class for all forms. If you notice, in the screenshot above you can see that the class ‘rsmModel’ extends FormRun class. So in order to call a form, we need to tell the system which specific form class to run. We can then assign it to the generic base class object. And call methods on the base class to make the form startup and run.
There are two different ways we can tell the system what form to open. We can either give it the name of the form directly. Or we can tell the system the name of a menu item to call, which as the form specified in the properties of the menu item.
Call The Form Directly
In order to call a form using x++ directly, add the following code to the clicked method of a button.
[Form]
public class rsmModel extends FormRun
{
[Control("Button")]
class FormButtonControlMake
{
/// <summary>
/// Call the rsmMake form
/// </summary>
public void clicked()
{
Args args;
FormRun formRun;
super();
//Call the form directly.
Args = new Args(formStr(rsmMake));
formRun = New FormRun(Args);
formRun.init();
formRun.run();
formRun.wait();
}
}
}
Note: In the above example code, the name of the outer class is named ‘rsmModel’. This is because that is the name of my form. Yours may be different, so it will use a different name there.
Also, in the above example, the inner class is named ‘FormButtonControlMake’. This is because that is the name of my Button control. If your button control is named differently, your class will have a different name.
Therefore, I recommend you just copy and paste the inside of the clicked method.
Explaining The Code
I will now explain the code.
First, I declare a couple of variables. One of type ‘Args‘, and another one of type ‘FormRun‘.
Secondly, I instantiate the Args class, passing into it the name of form that I want the button to open. Instead of just passing in a hard-coded string, I use the global function named ‘formStr‘ to get the string for the form named rsmMake. This is better than a hard-coded string, because if the name of the form changes this will cause a compile error. Whereas a hard-coded string will just cause an error at runtime.
Thirdly, I instantiate the FormRun class, passing in the Args object that contains the name of form I want to run. This is how I tell the FormRun class which form to open.
Lastly, I call three methods on the FormRun class. I call init to create and initialize all of the controls on the form. Next, I call the run method to allow the user to interact with the form. Finally, I call the wait method, which will cause the current form to not run any more code until the user closing the calling form.
Why Call A Form Using A Menu Item?
Another way we can call a form using x++ is to use the name of a menu item, instead of the name of the form. This may be preferable for a couple reasons.
- Menu Items are tied to Privileges and other security. And so a Menu Item cannot be called unless the user has a security role that ultimately allows the Menu Item to be used. Whereas if the form is called directly, I don’t believe any security is checked. (I need to double check this)
- In another article, I explained that Menu Item properties can be set to pass information to a calling form. Therefore, calling a menu item will still pass this information to a calling form. Sending this information can still be done using x++ code. However, using a Menu Item with this information already on it, may save you a couple lines of code.
Call A Form Using A Menu Item
In order to call a menu item in x++ code, use the following code.
[Form]
public class rsmModel extends FormRun
{
[Control("Button")]
class FormButtonControlMake
{
/// <summary>
/// Call the rsmMake form
/// </summary>
public void clicked()
{
Args args;
FormRun formRun;
super();
//Call the form using a Menu Item
args = new Args();
formRun = new menufunction(menuItemDisplayStr(rsmModel), MenuItemType::Display).create(args);
formRun.init();
formRun.run();
formRun.wait();
}
}
}
Notice that the code is very similar to calling a form directly. The difference is that instead of instantiating the FormRun class directly, we first call ‘new menufunction‘. This class takes two arguments.
First, it takes the name of the menu item. We get the name of the menu item by using the global function ‘menuItemDisplayStr‘. For the same reasons as before, this is better than hard-coding the name of the menu item.
Secondly, the class takes the type of menu item. When a form is being called, this should always be set to a ‘Display‘ menu item type.
After instantiating the MenuFunction class, we immediately call the method named ‘create‘ on this class. This method takes one optional parameter of type Args. We do not need to pass in Args in this example, but for the sake of future explanation I am passing in an empty Args class object. The ‘create’ method returns a FormRun object for us to use.
Finally, the same init, run, and wait methods are called on the FormRun class object.
The Advantage Of Calling A Form In Code
Until now, the x++ code we have written will work exactly the same as calling a Menu Item Button. But now I will show you why you may want to call a form using x++ code.
Using x++ code, a developer has full control over what values that are send into the Args class. The Args class can take several different pieces of information using methods on the class object. When using a Menu Item Button, these values of this Args object need to be hard-coded as values on the Menu Item properties. (One exception is that the currently selected record can be passed to the calling form).
In contrast, when calling a form using x++ code, the values in the Args class can be set programmatically. They do not always need to be the same value.
Sending Parameters To the Calling Form
There are several values on the Args class object that can be sent. Then, when the Args object is sent into the calling form, the calling form can read those values, and control the form based on these values. Typically, this information is used to filter the data source, hide or show controls, or display information such as a caption, on the calling form.
- parmEnumType and parmEnum on the Args class can be used to pass in an enum type and value to a calling form. These two methods are used together to ensure that the calling form knows why type of enum is being sent. This is useful if the information you have to send is an enum.
- parm on the Args class can be used to pass a string value to the calling form. This string value can then be used to control the calling form based on the string passed in.
- record is a method on the Args class that is used to pass in an entire table buffer record to the calling form. While this value is set automatically by the system whenever the DataSource property on a Menu Item Button is used, only one type of record can be sent this way. Whereas in code a developer could send into different types of table buffer records based on some conditional logic.
- caller is a method on the Args class that can be used to store the FormRun object of the form that is calling another form. This can be useful in case there are form variables that you would like to read.
ParmObject
Lastly, I wanted to explain parmObject separately. I have not seen it used very often. However, it can still be useful is some cases.
parmObject is a method on the Args class that I believe can only be set in x++ code. It is useful for passing in an entire class object to the calling form. Perhaps you have a lot of variables that you would like to pass to the calling form. More than you could pass just using parm and parmEnum. You could create a class that would contain all of the variables, and then pass this class object to the calling form using parmObject.
An Example
Below is an example of how the properties on the Args class can be sent. This is not meant to be a real functional example. It is not likely you would need to use all of these properties at the same time.
[Form]
public class rsmModel extends FormRun
{
[Control("Button")]
class FormButtonControlMake
{
/// <summary>
/// Call the rsmMake form
/// </summary>
public void clicked()
{
Args args;
FormRun formRun;
super();
//Call the form using a Menu Item
args = new Args();
//Send the value of IsServiceable to the calling form.
if (rsmModel.IsServiceable == NoYes::Yes)
{
args.parmEnumType(enumNum(NoYes));
args.parmEnum(NoYes::Yes);
}
else
{
args.parmEnumType(enumNum(NoYes));
args.parmEnum(NoYes::No);
}
//Send the model into the calling form.
args.parm(rsmModel.ModelID);
//Send the entire record into the calling form.
args.record(rsmModel);
//Send the entire form object to the calling form.
args.caller(element);
//Send an entire class object to the calling form
//This could be useful for sending multiple settings to the calling form.
TaxIntegrationHelper taxIntegrationHelper = new TaxIntegrationHelper();
args.parmObject(taxIntegrationHelper);
formRun = new menufunction(menuItemDisplayStr(rsmModel), MenuItemType::Display).create(args);
formRun.init();
formRun.run();
formRun.wait();
}
}
}
Conclusion
In most scenarios, a developer should use a Menu Item Button to call a form from another form. However, there are scenarios where a developer needs to have more control over what values are sent to the calling form. In these cases, it is useful to override the clicked method on a button, and call a form using x++ code. The developer can dynamically change what values are sent to the calling form. And the developer can even add code that runs after the calling form has been closed by the user. Even if you do not need to write code like this, it is helpful to understand this in case you read code on other forms that do use this technique.
As always, very helpful, amazing & informative article written by brilliant man.
Thank you Peter..
Brilliant article, very helpful.
Thank you, Peter.