This article describes event terminology and keywords in X++.
Events in X++ follow a design pattern that promotes modular, reusable code. The term event is used as an analogy for how delegates function. When a significant action occurs during execution, other parts of the system may need to respond—these actions are referred to as events.
When an event takes place, a notifier triggers notifications to all the registered event handlers (also known as subscribers). This process is called raising an event.
In X++, delegates—which facilitate event handling—can be declared not only in classes but also in tables, forms, or queries.
Terminology Overview
Term | Description |
---|---|
Event | A significant action that may require a response from other components. |
Notifier | The component responsible for sending the event notification. |
Subscriber | Methods or functions that register to listen for a specific event. |
Event Handler | The specific method that handles an event once it’s raised. |
Delegate-Related Keywords in X++
Keyword/Term | Example Code | Description |
---|---|---|
delegate | delegate myDelegate(str info) {} | Defines a delegate. The return type is always void , and no implementation is allowed inside the braces. |
eventHandler | myClassInstance.myDelegate += eventHandler(otherClass.myMethod); | Associates a method with a delegate. It’s not a function but a keyword that connects a handler to an event. |
Subscribe to delegate | myClassInstance.myDelegate += eventHandler(OtherClass::aStaticMethod); | Adds a static method to the delegate as a handler. |
Call a delegate | myClassInstance.myDelegate("Hello"); | Invokes the delegate, which calls all subscribed handlers in the order they were added. |
Example: Defining and Handling Events
class PointWithEvent
{
real x, y;
void new(real _x, real _y)
{
x = _x;
y = _y;
}
void move(real x_offset, real y_offset)
{
x += x_offset;
y += y_offset;
this.moved(abs(x_offset) + abs(y_offset));
}
delegate void moved(real distance) { }
}
class PointKeeper
{
public void createAndMove()
{
PointWithEvent point = new PointWithEvent(1.0, 2.0);
point.moved += eventhandler(this.writeMove);
point.move(4.0, 5.0); // Output: "9.0"
}
public void writeMove(real distance)
{
info(any2Str(distance));
}
}
Pre and Post Event Handlers
In earlier versions of X++, metadata could be used to run certain methods before or after another method. However, this wasn’t ideal for modern environments. Now, PreHandlerFor and PostHandlerFor attributes are used in code to define these behaviors.
Example:
[PreHandlerFor(classStr(MyClass2), methodStr(MyClass2, publisher))]
public static void PreHandler(XppPrePostArgs arguments)
{
int arg = arguments.getArg("i");
}
[PostHandlerFor(classStr(MyClass2), methodStr(MyClass2, publisher))]
public static void PostHandler(XppPrePostArgs arguments)
{
int arg = arguments.getArg("i");
int retvalFromMethod = arguments.getReturnValue();
}
public int Publisher(int i)
{
return 1;
}
This setup allows handling actions before and after Publisher
runs. However, these handlers can break if method signatures change or if the method is removed.
Using Attributes to Bind Event Handlers
Attributes can also link handlers to delegates directly, without writing subscription logic in code.
[SubscribesTo(
classStr(FMRentalCheckoutProcessor),
delegateStr(FMRentalCheckoutProcessor, RentalTransactionAboutTobeFinalizedEvent))]
public static void RentalFinalizedEventHandler(
FMRental rentalrecord, Struct rentalConfirmation)
{
}
delegate void RentalTransactionAboutTobeFinalizedEvent(
FMRental fmrentalrecord, struct RentalConfirmation)
{
}
In this case, the RentalFinalizedEventHandler
method is automatically triggered when the RentalTransactionAboutTobeFinalizedEvent
delegate is raised. Since subscriptions use attributes, you cannot control the order in which handlers are executed.