In the previous blog post we had a look at the possibilities of adding new methods and variables to existing AX7 program code using extensions. What if we need to modify, or add extra functionality to existing methods or events on existing classes, tables and forms? In that case we use AX7 events and delegates (also called ‘hooks’).

There are already plenty of events (hooks) we can subscribe in AX and extend the existing functionality without overlayering or overriding the function we need to extend. You can find those under the “Events” node of the application objects. Subscription is as easy as right clicking and selecting “Copy event handler method”, then you need to paste it into an eventhandler class and write your own logic inside:

public static class ExtensionDemoInventTableEventHandlers
{
[DataEventHandler(tableStr(InventTable), DataEventType::Deleted)]
public static void InventTable_onDeleted(Common sender, DataEventArgs e)
{
 InventTable inventTable = sender as InventTable;

//Your logic
}

}

Here I have subscribed to the ‘Deleted’ event of the table. Note that the object methods (like insert, update, init, write..) we used to override in the AX 2012 now corresponds to two different events, as ‘Deleting’ and ‘Deleted’, and we no more call super() in our event handler methods. The execution of those compared to AX2012 works like below:

public void delete()
{
.... //Existing code before super
Event_onDeleting();
super();
Event_onDeleted();
.... //Existing code after super
}

As a good practice you can place those event handlers in a static class which you suffix with ‘EventHandlers’ (which is not mandatory), or inside the extension class you have created for that object. On all events you subscribe you also have a parameter that contains an “event args” object for the corresponding event. However this parameter is not always the event args class you exactly need.. They are generally downcasted to their most common base class (DataEventArgs for data related events or FormEventArgs for events on the form root) and you need to cast it back to the args class you need. See the validatedFieldValue eventhandler below as an example :

[DataEventHandler(tableStr(InventTable), DataEventType::ValidatedFieldValue)]
public static void InventTable_onValidatedFieldValue(Common sender, DataEventArgs e)
{
 InventTable inventTable = sender as InventTable;
 ValidateFieldValueEventArgs ve = e as ValidateFieldValueEventArgs;
 boolean ret = ve.parmValidateResult();

 if(ret)
 {
 switch(ve.parmFieldName())
 {
 case fieldStr(InventTable,Description5):
 if(InventTable.Description5=="")
 {
 ret = false;
 }
 break;
 }
 }

ve.parmValidateResult(ret);
}

Capture9

There are a lot of event handlers with downcasted parameters like this one and it is impossible to list all of them here. You can investigate existing code for that and I recommend you to create yourself a template document with templates to cast the Args parameters or reach other objects using the args or “sender” parameters within the eventhandler (if I grow one, i will share it here).

Beyond these events listed inside the Events node, there are Pre and Post event handlers which you can subscribe to existing public methods, and run your own logic before or after the original method is executed. And it is possible to change the return type and access the method parameters using the XppPrePostEventArgs parameter like the example below:

[PreHandlerFor(tableStr(InventTable), tableMethodStr(InventTable, defaultField))]
public static void InventTable_Pre_defaultField(XppPrePostArgs args)
{
 InventTable inventTable = Args.getThis() as InventTable;
 FieldId parmFieldId = Args.getArg('_fieldId');

 switch(parmFieldId)
 {
 case fieldNum(InventTable, Description5) :
 InventTable.Description5 = '';
 break;
 }

}

[PostHandlerFor(tableStr(InventTable), tableMethodStr(InventTable, isBOMAllowed))]
public static void InventTable_Post_isBOMAllowed(XppPrePostArgs args)
{
 InventTable inventTable = Args.getThis() as InventTable;
 boolean ret = Args.getReturnValue();

 if(ret)
 {
  ret = (inventtable.Description5 != "");
 }

Args.setReturnValue(ret);
}

By default, you cannot subscribe to protected or private methods, but there is a workaround to be able to subscribe those. You can customize the object that holds the method by overlayering and add the following attribute on top of the method. Then you will be able to add pre and post handlers to that method regardless of protected or private modifier :

 [HookableAttribute(true)]
protected boolean parmHasArgs(boolean _hasArgs = hasArgs)
{
 hasArgs = _hasArgs;
 return hasArgs;
}

To be able to query existing eventhandlers one event has, simply right click and select “Find event handlers” :

Capture8

We have seen how to use existing events and how to add extended code before or after a method call. What if we need to modify a method by inserting program code in the middle of its program code? In that case we use delegate methods just as in AX2012. Subscribing to a delegate function is as shown below:

Capture10

Then paste it into your extension, or event handler class:

 [SubscribesTo(classStr(LedgerJournalCheckPost), delegateStr(LedgerJournalCheckPost, runInternalPostPostJournalDelegate))]
 public static void LedgerJournalCheckPost_runInternalPostPostJournalDelegate(LedgerJournalCheckPost _ledgerJournalCheckPost, boolean _postingSuccess, boolean _intercompanyPostingSuccess)
 {
 //Your own logic
 }

There are already many delegate functions in key classes published by Microsoft and we can add new ones by overlayering these classes ourselves. If you do not want to use overlayering, in the time of writing, there is an option to book a request in Microsoft in Visual Studio Online and ask them to add a delegate to a system method for you, which is not accessible for everyone (I think only for the companies in the development program). You can ask your administration if this is available for your company. If you would like to create a delegate in your own code to allow subscription, you declare it like below. Then from the designer window of your class you can subscribe to your own delegate:

class ExtensionDemoDelegateTest
{
void new()
{
}

public void showMessages()
{
Description message='Internal message text';

info('Before..');

this.showMessageText(Message);

info('After...');

}

delegate void showMessageText(Description _msg) {}

public static void main(Args _args)
{
ExtensionDemoDelegateTest test;

test = new ExtensionDemoDelegateTest();

test.showMessages();
}

}

Capture13.PNG

static class ExtensionDemoDelegateTestEventHandlers
{

//Handler 1
[SubscribesTo(classStr(ExtensionDemoDelegateTest), delegateStr(ExtensionDemoDelegateTest, showMessageText))]
public static void ExtensionDemoDelegateTest_showMessageText(Description _msg)
{
info('Message echo from the event handler: '+_msg);
}

//Handler2
[SubscribesTo(classStr(ExtensionDemoDelegateTest), delegateStr(ExtensionDemoDelegateTest, showMessageText))]
public static void ExtensionDemoDelegateTest_showMessageText2(Description _msg)
{
info('Message echo from the event handler 2: '+_msg);
}}

When we run the class :

Capture14

As you see we can add as much hooks (or eventhandlers) as we want and they will run when the delegate is called inside the class method. Remember in AX, just like .NET, there is no guarantee on execution order of the eventhandlers subscribed to a delegate method. So never assume in your code that the first eventhandler you write will execute before the second or so, that is not guaranteed.

That is all about the event handlers in this blog. For more information on event handlers and delegates, you can check the wiki articles at :

https://ax.help.dynamics.com/en/wiki/xpp-classes-and-methods/#event-terminology-and-keywords

https://ax.help.dynamics.com/en/wiki/customization-overlayering-and-extensions

 

5 thoughts on “AX7 Extensibility – Part 3 : Event handlers and delegates (hooks)

  1. Thank you for the useful blog post! I’m learning programming with X++ and D365 (never used AX2012 or other OO languages) and these are fantastic examples to help me understand how the parameters are used in event handlers.

    I would like to ask, how do you do your code samples? I’m using GIST for my blog, and whatever you are doing looks much nicer.

    Thank you!

    1. Hi: Thank you for your review.

      I used to have another website for my blogs but posting code samples was a problem too. There are free libraries for that but you always need to do your own maintenance yourself. Therefore I decided to switch over WordPress platform. In wordpress you can add code blocks with just tags, thats all you need, and it is lightweight and always maintained for free.
      Currently basic wordpress hosting is a free service with those amazing capabilities, but do not know for how long it is going to stay like that…
      Good luck with your blog.

Leave a Reply