First
method is of course the legacy method of creating a derived class which we want
to extend, adding or overriding base methods in it and use this class in our
new model instead of the base class. This solves many of the customization work
we need but there might be cases this method is not desirable or we cannot
modify the code of existing tables and forms like that.
Second
option is to create something new called an ‘extension class’ to extend the
program code of an existing object. This is simply done by creating an
extension class with the template shown below using the ‘ExtensionOf’
attribute:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[ExtensionOf(classStr(MyClass))]
final class
MyClass_Extension
{
private int myParm;
private void new()
{
}
public int parmMyParm(int _myParm = myParm)
{
myparm = _myParm;
return myParm;
}
public static MyClass constructFromMyParm(int _myParm)
{
MyClass myClass;
myClass = new
MyClass();
myClass.parmMyParm(_myParm);
return myClass;
}
}
|
This
way you can add new methods, variables, constructors and static methods
directly added to the base class instead of using a derived class. After this,
you can call the new methods and constructors you have added in your extension
class directly from the base class itself, such as:
MyClass::constructFromMyParm(24).
The
suffix of the extension class you have created must be ‘_Extension’ and you can
name the MyClass part with anything you want, it does not have to be the base
class name. Same way you can create code extensions for tables and forms (as of
version update2) as well, later we will try an example with each.
Although
this looks like a derived class construction, the extension class is not a
derived class from the base and you cannot access private and protected methods
and variables, nor override existing methods of the base class here. But it is
possible to access public methods and variables of the base class as well as
using ‘this’ keyword in your method declarations to access those. It is also
possible to access the methods of other extensions on the same base object if
you have added the model that includes the other extension class in your model
definition’s referenced packages.
I also
want to mention the extension class template below. Before the “Update1”
version of AX7, the template for creating the extension class for tables and
classes was like below :
1
2
3
4
5
6
7
8
|
public static class MyClass_Extension
{
public static int parmMyParm(MyClass
_this, int _myParm
= myParm)
{
myparm = _myParm;
return myParm;
}
}
|
Here
you declare your class and all the class methods as static (class with
_Extension suffix) and add the object you want to extend as the first parameter
of your method. I do not recommend using this template in your new program code
because the new one is more readable and useful, but be aware: On
runtime your new notation extension class works exactly the same way using
static method calls, just like the old notation did. This is why you cannot
access class private and protected variables nor override existing methods
in it. Also for the same reason you should not create a direct instance
of the extension class in your program code, nor create another class
derived from your extension class (it also receives a “sealed”
attribute when built into CIL, like other static classes in .net). You can
confirm it for yourself using visual studio debugger:
Creating
table methods is pretty the same, you can even create new display and edit
methods as shown below, to be used in a form (or form extension) in the same
model :
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[ExtensionOf(tableStr(InventTable))]
final class
ExtensionDemo_Extension
{
private void new()
{}
[SysClientCacheDataMethodAttribute(true)]
public display Description dispExtendedDesc()
{
return this.Description5+this.Description6;
}
}
|
To be
able to use this display method in your form, you need to define the “Data
method” property of your data field like this, using static method operator and
name of your extension class (“ExtensionDemo_Extension::dispExtendedDesc”).
Directly writing the method name will not work :
Form
extensions are created pretty much the same way. The newly added methods are
accessible from the form and its extensions. The ‘this’ object in your
extension contains the element object (FormRun) of the extended form, so you
can query for elements and data sources easily from here. Here is an example
how to use element variables in a form extension class in various different
ways :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
[ExtensionOf( formstr( ExtensionDemoForm ))]
final class
ExtensionDemoForm_Extension
{
private
boolean hasArgs;
private
void new()
{}
public
boolean parmHasArgs(boolean _hasArgs = hasArgs)
{
hasArgs = _hasArgs;
return
hasArgs;
}
public
boolean hasArgsRecord()
{
return
(this.args().record()!=null);
}
[FormEventHandler(formStr(ExtensionDemoForm),
FormEventType::Initialized)]
public
void ExtensionDemoForm_OnInitialized(xFormRun
sender, FormEventArgs e)
{
this.parmHasArgs((this.args()!=null));
}
}
|
Note
that the event handler here does not have the ‘static’ keyword, but instead
declared as a class instance method intentionally removing the static keyword
from the definition. It is possible to declare some of the event handlers like
that then you can use ‘this’ keyword to access methods and variables defined in
the same class from a eventhandler (more on eventhandlers on Part3).
You can
add a simple click method and test our extension method inside the form :
|
public void clicked()
{
super();
info(strFmt("Has args?: %1",
element.parmHasArgs()));
info(strFmt("Has args record?: %1",
element.hasArgsRecord()));
}
|
Eventually
it is also possible to override form control and data field methods from the
form extension class, which gives you much more flexibility in creating
extensions for forms:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[ExtensionOf( formstr( ExtensionDemoForm ))]
public final class
ExtensionDemoForm_Extension
{
public void clickedOverride()
{
info(strFmt("Name : %1", this.name()));
}
[FormEventHandler(formStr(ExtensionDemoForm),
FormEventType::Initialized)]
public void ExtensionDemoForm_OnInitialized(xFormRun
sender, FormEventArgs e)
{
FormButtonControl button =
this.design().controlName(formControlStr(ExtensionDemoForm,FormButtonControl1));
button.registerOverrideMethod(methodStr(FormButtonControl,clicked),'clickedOverride',
this);
}
}
|
A note
on form class extensions: In this time of writing (Update 3 version), not
everything with form class extensions work all right, it has some bugs which
are already reported to Microsoft. For example the code above compiles ok but
on runtime the method above is not accesible from the Formrun class, although
the previous example compiles and runs just fine (just using ‘this’ keyword in
the same class). I will post an update if things change in the future on that
part.
Plugins
Plugins
are yet another way to extend code in new AX7 besides the extension methods we
mentioned above. Plugins use attributes to create new subclasses and subclass
methods which can be used as alternatives to existing classes and methods
belonging to same plugin base and can be initialized using a string type of key
from the SysPluginFactory class. This way you can also choose which plugin to
use by selecting it with a string parameter. I will not get into the details of
using plugins and the attributes used in plugin framework but perhaps I might
write another blog about them in the future.
This comment has been removed by a blog administrator.
ReplyDeleteThis blog is very helpful. Thanks for sharing.
ReplyDeleteD365 finance and operations online training
Microsoft Dynamics AX Technical Training
will omit your great writing due to this problem.
ReplyDeletejava training
java online training