Showing posts with label xpo. Show all posts
Showing posts with label xpo. Show all posts

Thursday, September 23, 2010

Tutorial: Undocumented behavior of kernel functions min()/max()

I was recently reviewing some code written to be shipped with AX6, and noticed an unfamiliar pattern being used in it. I investigated a bit deeper, and turns out it actually works fine on previous versions of AX as well.

I am talking about 2 kernel functions for finding the maximum or minimum of the specified values.
The signature of these methods is shown on the below image:

As you can see, it takes 2 arguments of anytype, and returns an anytype which is the largest of the two values. But it can accept much more than 2 arguments, even though it is not documented as such.

I wrote a small job to showcase this behavior. The code is provided below. You can also download it from my SkyDrive Dynamics AX share.
static void Tutorial_MinMaxFunctions(Args _args)
{
#define.ArraySize(11)

Random rand = new Random();

int counter;
int arrayInt[#ArraySize];
str arrayIntAsString;
int arrayIntMaxValue;
int arrayIntMinValue;
;

for (counter = 1; counter <= #ArraySize; counter++)
{
arrayInt[counter] = rand.nextInt();
if (arrayIntAsString)
arrayIntAsString += ', ';
arrayIntAsString += int2str(arrayInt[counter]);
}
info("Generated array of integers: " + arrayIntAsString);

info("The typical way to find a maximum is by looping through all the values one by one, calling the comparison function multiple times");
arrayIntMaxValue = minint();
arrayIntMinValue = maxint();
for (counter = 1; counter <= #ArraySize; counter++)
{
arrayIntMaxValue = max(arrayIntMaxValue, arrayInt[counter]);
arrayIntMinValue = min(arrayIntMinValue, arrayInt[counter]);
}
info(strfmt("Max.value: %1 and Min.value: %2", int2str(arrayIntMaxValue), int2str(arrayIntMinValue)));

info("Using max and min with 11 arguments works just as well");
arrayIntMaxValue = minint();
arrayIntMinValue = maxint();
arrayIntMaxValue = max(arrayInt[1], arrayInt[2], arrayInt[3], arrayInt[4], arrayInt[5], arrayInt[6], arrayInt[7], arrayInt[8], arrayInt[9], arrayInt[10], arrayInt[11]);
arrayIntMinValue = min(arrayInt[1], arrayInt[2], arrayInt[3], arrayInt[4], arrayInt[5], arrayInt[6], arrayInt[7], arrayInt[8], arrayInt[9], arrayInt[10], arrayInt[11]);
info(strfmt("Max.value: %1 and Min.value: %2", int2str(arrayIntMaxValue), int2str(arrayIntMinValue)));

info("Note that comparing an integer and a real also works, as well as outputing the results straight into an infolog message");
info(max(12, 12.001));
}

Another interesting point is that it can actually accept different types of arguments, for example, a real and an integer, as shown above. And it actually returns an anytype, which implicitly gets converted to a string when sent to the infolog.

Disclaimer: Since this is not a documented feature, it can theoretically change in the future releases, but I doubt it in this particular case.

Monday, August 24, 2009

“Go to main table” on a RunBase dialog control

In one of my blog posts, I have been posed with the following question:

“how to achieve go to main table on dialog field.
for example a dialog is having a field of lookup custAccount, now what i need to have a go to main table functionality on the same.”

So, the short answer is: It is not possible to override jumpRef method directly on the dialog control. This is due to the fact that in order to show the “Go to Main table” link in the context menu of a control, you need to actually override that method on the form, which is later used to draw the context menu.

There are 3 possible ways that you can achieve the same functionality though, that I would like to briefly discuss here:

  1. Recommended Don’t bother with the dialog. Simply create a form, and override the jumpRef method on the needed control on the form. To demonstrate this behavior, you can use the Tutorial_RunBaseForm class and form. Override the jumpRef method, put the code to call the main form there. And you are done. Simple, and does not require a lot of coding. There is a lot of confusion, where junior AX developers think it will require a lot of code changes to replace a dialog with a form. This is not entirely that hard, as RunBase classes can use a regular AOT form instead of the regular dialog.
  2. Use controlMethodOverload approach, and create the controlName_context() method, which is executed when you try to open the context menu on the specified control. Inside this method, build the context menu from scratch. This way, only the items you want to be shown will be present in the context menu. Note: this means, that standard context menu items, like Setup, will not be shown in the menu (unless you add and handle them).
  3. Use controlMethodOverload approach, as in the previous example, but instead of overriding context() method, override the showContextMenu method. This will allow you to add your custom context menu items to the standard context menu. This does not seem to work on Axapta 3.0 though, and I did not try to figure out why. Note you need to uncomment some code in order to enable this approach (I left a TODO comment for that).

I have made the necessary changes, demonstrating the latter 2 approaches, using the class located under Classes\Tutorial_RunBaseBatch. You can download the xpo here (it contains only the USR layer changes).

The class used for drawing the context menu in the example is called PopupMenu. A good example of how this class can be used in the application is available in AOT under Forms\Tutorial_PopupMenu. Some information, along with another example, is available on MSDN.

A while ago, I have suggested some minor changes to the Dialog framework classes, which allow to simplify the process of overriding control event methods on the dialog. The original post can be found through this link: http://microsoftok.blogspot.com/2007/06/3-dialog-extensions.html

Let me know if any of this requires more clarification.