X++ gists for D365FO

A collection of X++ gists for those occasions when the help pages don’t cut to the chase!

Extensions

It is important to following the naming guidelines when extending an existing object to ensure uniqueness across the Application Object Tree over time.

Table extensions

Additional methods are added to an existing table by creating a new class with an attribute declaring that it extends the existing table:

/// <summary>
/// Form extensions should have an Extension suffix, and should include the name
/// of the extending model as an infix to maintain uniqueness. The extension class
/// must be marked as final as it would make no logical sense to subclass
/// or extend an extension.
/// </summary>
[ExtensionOf(tableStr(HcmWorker))]
public final class HcmWorker_MyNewModel_Extension
{
public display Name someOtherName()
{
return 'someOtherName';
}
}
view raw xpp hosted with ❤ by GitHub

Class extensions

Additional behaviours are added to existing class methods by creating a new class with an attribute declaring that it extends the existing class:

/// <summary>
/// Class extensions should have an Extension suffix, and should include the name
/// of the extending model as an infix to maintain uniqueness. The extension class
/// must be marked as final as it would make no logical sense to subclass
/// or extend an extension.
/// </summary>
[ExtensionOf(classStr(CustTransSettlement))]
final class CustTransSettlement_MyNewModel_Extension
{
protected boolean isTransTypeValidForInsertSettlementLines(CustTrans _custTrans)
{
//We must always call the next handler in the Chain of Command (CoC) even if
//we really don't wan't or need to.
//The only guarantee about sequence is that the standard version will run last.
next isTransTypeValidForInsertSettlementLines(_custTrans);
//Override the standard result with our own logic, but put the logic somewhere else
//to keep this method assimple as possible to protect against future
//framework changes.
return MyOtherClass::SomeOtherMethod(_custTrans);
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

Delegates

Use multicast delegates to create extension points for other developers to subscribe to.

void myMethod()
{
EventHandlerResult result = new EventHandlerResult();
this.nydelegate(1, 2, result);
var total = result.result();
}
delegate void myDelegate(real parm1, real parm2, EventHandlerResult result)
{
//Must be protected, void and empty!
//Add an EventHandlerResult to allow the subscriber to return a value
}
view raw xpp hosted with ❤ by GitHub

Remember that the order in which the handlers receive the invocation isn’t predictable.

Event Handlers

Take care not to add event handlers to a class that is already extending a table as the compiler will incorrectly add these methods to the Common object.

Delegate events

Subscribing to a multicast delegate:

[SubscribesTo(classStr(MyDelegateClass) delegateStr(MyDelegateClass, myDelegate))]
public static void delegateExample_myDelegate(real a, real b, EventHandlerResult result)
{
real total = a+b;
result.result(total);
}
view raw xpp hosted with ❤ by GitHub

Form events

Handling a form event:

[FormEventHandler(formstr(FMVehicle), FormEventyType::Inititialized)]
public static void FMVehicle_OnInitialized(xFormRun sender, FormEventArgs e)
{
FormDataSource ds = sender.dataSource(formDataSourceStr(FMVehicle, FMVehicle)
//etc..
}
view raw xpp hosted with ❤ by GitHub

Form data-source events

Handling a form data-source event:

[FormDataSourceEventHandler(formDataSourceStr(FMVehicle, FMVehicle),
FormDataSourceEventType::written)]
public static void FMVehicle_OnWritten(FormDataSource sender, FormDataSourceEventArgs e)
{
FormRun formRun = sender.formRun() as FormRun;
//etc..
}
view raw xpp hosted with ❤ by GitHub

Form control events

Handling a form control event:

Table events

Handling a table event:

[DataEventHandler(tableStr(FMVehicle), DataEventType::ValidatedWrite)]
public static void FMVehicle_onValidatedWrite(Common sender, DataEventArgs e)
{
ValidatedEventArgs validateArgs = e as ValidatedEventArgs;
FMVehicle vehicle = sender as FMVehicle;
boolean result = validateArgs.parmValidateResult();
if (vehicle.NumberOfCylinders == 0)
{
result = checkFailed('Invalid number of cylinders.');
validateArgs.parmValidateResult(result);
}
}
view raw xpp hosted with ❤ by GitHub

Enumerations

Iterating enums

Regardless of the language, you should only ever reference an enum by its elements (and not it’s intrinsic value).  Historically, X++ developers have sometimes referred to the enumeration’s intrinsic values.  This is no longer supported in D365FO as these values are determined at runtime.

DictEnum dictEnum = new DictEnum(enumNum(ProjFundingType));
if (dictEnum)
{
int enumValueCount = dictEnum.values();
//Iterate on positions, not values!!
for (int enumValueIndex = 0; enumValueIndex < enumValueCount; enumValueIndex++)
{
if (dictEnum.index2Value(enumValueIndex) != ProjFundingType::Customer)
{
doSomething(dictEnum.index2Value(enumValueIndex);
}
}
}
view raw DictEnum.xpp hosted with ❤ by GitHub

Using enums in a switch

We can also use the elements from an enum in a switch statement:

SysDictEnum dictEnum = new SysDictEnum(enumNum(ProjSalesPriceMarkup));
int enumValueCount = dictEnum.values();
for (int enumValueIndex = 0; enumValueIndex < enumValueCount; enumValueIndex++)
{
ProjSalesPriceMarkup value = dictEnum.index2Value(enumValueIndex);
str actual = dictEnum.index2Label(enumValueIndex);
str expected = strMin();
switch (value)
{
case ProjSalesPriceMarkup::No :
expected = "@SYS26062";
break;
case ProjSalesPriceMarkup::Yes :
expected = "@SYS80120";
break;
case ProjSalesPriceMarkup::MarkupPct :
expected = "@SYS78909";
break;
default :
break;
}
}
view raw xpp hosted with ❤ by GitHub

Working with XML

Iterating a list of XML nodes

public boolean elementExists(str elementName, str attributeName, str attributeValue)
{
XmlNodeList nodes = xmlDocument.GetElementsByTagName(elementName);
XMLNodeListIterator iterator = new XMLNodeListIterator(nodes);
while (iterator.moreValues())
{
XmlElement e = iterator.value();
str val = e.getAttribute(attributeName);
if (val == attributeValue)
{
return true;
}
iterator.nextValue();
}
return false;
}

More gists to follow…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: