X++ (Microsoft Dynamics Ax - Axapta)
Frequently Asked Questions (exontrol)
XPP.0:
In Microsoft Dynamics Ax 32-bit you can use any of the following versions:
  • /COM indicates the 32-bit edition of the ActiveX version

The application built using /COM version runs on any Windows 32 or 64-bit machine.

In Microsoft Dynamics Ax 64-bit you can use any of the following versions:

  • /COM/64 indicates the 64-bit edition of the ActiveX version

The application built using /COM/64 version runs on Windows 64-bit machine only. The application built using /COM/64 version cannot run on Windows 32-bit machine.

If you want to use your application on 32 and 64-bit machines, you can go for:

  • /COM/ANY indicates the 32 and 64-bit editions of the ActiveX versions

Should I use a 32-bit or 64-bit version of the control?

XPP.1:
The Microsoft Dynamics Ax supports ActiveX controls, so you need to install the /COM version of the component. Go to download page and click the component you need install in the ActiveX/COM column ( 32 bit ). Click the link, download the component, and the Run it. The installation setup will install samples, documentation and the /COM version of the component, that can be used in any Ax forms.
XPP.2:
Click the Application Object Tree (AOT), so the AOT window is opened. Open the Forms folder, and then create a new form, or select an existing one. Expands the form being selected, so you can see the Designs item, and locate the Design node. Right-click the Design node for the form, and then select New Control. Select ActiveX from the submenu. Choose the control that you want to insert, let's say, ExG2antt Chart ActiveX Control. Click the Properties of the newly ActiveX member inserted, and go to the Name and type exg2antt1, so you assign a name for your component.
XPP.3:
Generally, the eXHelper tool generates the form's init method. In other words, you need to override the init method of your form ( where the control is being inserted or hosted ). In the AOT window, select the Form that hosts the control, expand the node, so you can see the Methods node. Right Click the Methods node, and select the Override Method from submenu, and init from the next submenu. So, your Ax environment will generate the following code:
public void init()
{
    super();
}
Now, open the exhelper tool. Select the component you want to copy code from in the left side drop down. Click the Help button, so the questions are loaded, and the control is read to be used in the exhelper. Select the question you want to copy the code, and then double click it, or press ENTER key. The component panel will show the control in running state, and the middle panel shows the x-script language you want to convert. Click the ... button on the horizontal scroll bar  (middle panel), and select the X++ (Dynamics Ax 2009), so the middle panel will show the X++ code you can copy and paste directly on your form's init method.
XPP.4:
In the AOT window, select the Form that hosts our component. Expand it, so you can locate the Designs\Design\ActiveX:. Right click the ActiveX node ( under the Design node ),  and select the ActiveX Explorer from the submenu, so the list of events and methods for the component is displayed. In the Event tab page locate the event you need to handle, and click the Add, and then Edit, so the Ax environment adds the empty handler for you. 

For instance, let's say you need to handle the BeforeExpandItem event of the eXG2antt component, so you need to locate the void onEvent_BeforeExpandItem(int _Item, COMVariant /* variant */ _Cancel).

// Fired before an item is about to be expanded (collapsed).
void onEvent_BeforeExpandItem(int _Item, COMVariant /* variant */ _Cancel)
{
}

Another way is using the the Event which notifies your application once a new event is fired by the control. The EventParam property gets the value of specified parameter during an event, and it is valid ONLY during the control's event. The definition for all events can be found on the control's documentation. 

The following sample displays information about the firing event:

// Event event - Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
	;
	print(exg2antt1.EventParam(-2));
}

The EventParam property can be used as follows:

  • The EventParam(-2) to gets the entire information about fired event ( such as name, identifier, and parameters ) as a string.
  • The EventParam(-1) retrieves the number of available parameters for current event.
  • The EventParam(Index) retrieves the parameter with specified Index, where index should be greater or equal with Zero and less than number of parameters for the current event

During the Event handler you can use the EventParam to access the parameters of the current event.

For instance the following X++ sample displays the name of the bar being resized, in the eXG2antt control:

// Event event - Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
	;
	// event BarResize (Item as HITEM, Key as Variant) 
	if (_EventID == 120)
	{
		print(exg2antt1.ExecuteTemplate("Items.ItemBar(Me.EventParam(0),Me.EventParam(1),0)"));
	}
}
where the 120 is the identifier of the BarResize event ( which can be determined using the EventParam(-2) ), the ExecuteTemplate property provides the result of executing the x-script template at runtime. The Items.ItemBar(Item,Key,Property) returns the value for a specified property of a specified bar being indicated by Item and key. The Item parameter of the BarResize is getting by the EventParam(0), the Key using the EventParam(1), and 0 indicates the identifier for the exBarName property. In conclusion, the sample displays the name of the bar being resize or moved using the x-script and ExecuteTemplate method.
XPP.5:
Generally, the parameters passed by reference are of COMVariant type. Our component provide the EventParam method that can be used to access or change the parameters of the event, as described in the following samples. For instance, let's say you need to handle the BeforeExpandItem event of the eXG2antt component, which has the Cancel parameter which indicates whether the control should expand / collapse an item. Setting this parameter on False, will prevent the control to expand or collapse the item. So, generally you will write the code:
// Fired before an item is about to be expanded (collapsed).
void onEvent_BeforeExpandItem(int _Item, COMVariant /* variant */ _Cancel)
{
    ;
    _Cancel = True;
}

but it will gives the compiler error: *** Error: 1, Operand types are not compatible with the operator. So, the next step is to correct the error. Since the _Cancel parameter is of COMVariant type as seen in the event's declaration, we need to make sure that the value being passed to _Cancel parameter will be a COMVariant type, not a boolean directly, so we need to lool for static members of the COMVaraint type ( just type COMVariant::, and the environment will show you the list of static members you can use ). In our case we should look for COMVariant::createFromBoolean which will do the trick, so the code will shows as:

// Fired before an item is about to be expanded (collapsed).
void onEvent_BeforeExpandItem(int _Item, COMVariant /* variant */ _Cancel)
{
    ;
    _Cancel = COMVariant::createFromBoolean(True);
}

Now, the code is compiled just fine, but still if we run the sample, the user is still able to expand or collapse an item. Now, we reach the conclusion, that actually the Dynamics Ax can not handle, or pass back to the client, the changed value. Our component provide the EventParam method that can be used to access or change the parameters of the event. So the code will show as:

// Fired before an item is about to be expanded (collapsed).
void onEvent_BeforeExpandItem(int _Item, COMVariant /* variant */ _Cancel)
{
    ;
    exg2antt1.EventParam( 1, COMVariant::createFromBoolean(True) );
}

The first parameter of the EventParam method indicates the index (0 based) of the parameter being accessed. In this case, the event has two parameters, the first _Item with the index 0, and the _Cancel with the index 1. 

The following X++ samples displays the start point of the bar being resized, in the eXG2antt control:

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
    COM com_Items;
    anytype var_Items;
    COMVariant vStart;
    date dtStart;
    ;

    // event BarResize (Item as HITEM, Key as Variant)
    if (_EventID == 120)
    {
    
        var_Items = exg2antt1.Items(); com_Items = var_Items;
        vStart = com_Items.ItemBar(exg2antt1.EventParam(0),exg2antt1.EventParam(1),1);
        dtStart = vStart.date();

        print(dtStart);
    }

}

The following X++ samples displays the start point of the bar being resized, in the eXG2antt control ( similar with the previously sample, excepts that it uses the ExecuteTemplate property):

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
    str sResult;
    COMVariant vStart;
    date dtStart;
    ;

    // event BarResize (Item as HITEM, Key as Variant)
    if (_EventID == 120)
    {
        vStart = exg2antt1.ExecuteTemplate("Items.ItemBar(Me.EventParam(0),Me.EventParam(1),1)");
        dtStart = vStart.date();
        
        print(dtStart);
    }

}
XPP.6:
The control provides Template and ExecuteTemplate properties that can be used to execute x-script templates. Shortly, an x-script is a string that control can execute. The Template method does not return any result, while the ExecuteTemplate property returns the last result from the x-script. Both commands are cumulative, in other words. For instance calling the Template("Columns.Add(0)") multiple times, adds a new column for each call. So, calling the Template/ExecuteTemplate property does not reset the control.

For instance, the following sample changes the control's background color using x-script and Template property:

public void init()
{
	;

	super();

	exg2antt1.Template("BackColor = RGB(255,0,0)");
}

The following sample displays the BackColor's property value using the ExecuteTemplate property

public void init()
{
    COMVariant vResult;

    ;

    super();

    vResult =exg2antt1.ExecuteTemplate("BackColor");
    print(vResult.toString());

}

In conclusion, the Template property can execute any x-script code without returning any result, while the ExecuteTemplate property executes and returns the last result of the code.

For instance the following X++ sample displays the name of the bar being resized, in the eXG2antt control:

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
    COMVariant vResult;
    ;

    // event BarResize (Item as HITEM, Key as Variant)
    if (_EventID == 120)
    {
        vResult = exg2antt1.ExecuteTemplate("Items.ItemBar(Me.EventParam(0),Me.EventParam(1),0)");
        print(vResult.toString());
    }

}

where the 120 is the identifier of the BarResize event ( which can be determined using the EventParam(-2) ), the ExecuteTemplate property provides the result of executing the x-script template at runtime. The Items.ItemBar(Item,Key,Property) returns the value for a specified property of a specified bar being indicated by Item and key. The Item parameter of the BarResize is getting by the EventParam(0), the Key using the EventParam(1), and 0 indicates the identifier for the exBarName property. In conclusion, the sample displays the name of the bar being resize or moved using the x-script and ExecuteTemplate method.

For instance the following X++ sample displays the start point of the bar being resized, in the eXG2antt control:

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
    str sResult;
    COMVariant vStart;
    date dtStart;
    ;

    // event BarResize (Item as HITEM, Key as Variant)
    if (_EventID == 120)
    {
        vStart = exg2antt1.ExecuteTemplate("Items.ItemBar(Me.EventParam(0),Me.EventParam(1),1)");
        dtStart = vStart.date();
        
        print(dtStart);
    }

}
where the 120 is the identifier of the BarResize event ( which can be determined using the EventParam(-2) ), the ExecuteTemplate property provides the result of executing the x-script template at runtime. The Items.ItemBar(Item,Key,Property) returns the value for a specified property of a specified bar being indicated by Item and key. The Item parameter of the BarResize is getting by the EventParam(0), the Key using the EventParam(1), and 0 indicates the identifier for the exBarStart property. In conclusion, the sample displays the starting point of the bar being resize or moved using the x-script and ExecuteTemplate method.
XPP.7:
This may occurs when handling events that have parameters passed by reference. Passed by reference, means that in the event handler, you can change the value for that parameter, and so the control will takes the new value, and use it. The X++ is NOT able to handle properly events with parameters by reference, so we have the solution. 

The solution is using and handling the Event notification and EventParam method., instead handling the event that gives the "invalid parameters" error executing code.

Let's presume that we need to handle the BarParentChange event to change the _Cancel parameter from false to true, which fires the "Error executing code: FormActiveXControl (data source), method onEvent_BarParentChange called with invalid parameters." We need to know the identifier of the BarParentChange event ( each event has an unique identifier and it is static, defined in the control's type library ). If you are not familiar with what a type library means just handle the Event of the control as follows:

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
	print exg2antt1.EventParam(-2).toString();
}

This code allows you to display the information for each event of the control being fired as in the list bellow:

"MouseMove/-606( 1 , 0 , 145 , 36 )" VT_BSTR
"BarParentChange/125( 192998632 , 'B' , 192999592 , =false )" VT_BSTR
"BeforeDrawPart/54( 2 , -1962866148 , =0 , =0 , =0 , =0 , =false )" VT_BSTR
"AfterDrawPart/55( 2 , -1962866148 , 0 , 0 , 0 , 0 )" VT_BSTR
"MouseMove/-606( 1 , 0 , 145 , 35 )" VT_BSTR

Each line indicates an event, and the following information is provided:  the name of the event, its identifier, and the list of parameters being passed to the event. The parameters that starts with = character, indicates a parameter by reference, in other words one that can changed during the event handler. 

Now, we can see that the identifier for the BarParentChange event is 125, so we need to handle the Event event as:

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
    ;
    if ( _EventID == 125 ) /*event BarParentChange (Item as HITEM, Key as Variant, NewItem as HITEM, Cancel as Boolean) */
        exg2antt1.EventParam( 3 /*Cancel*/, COMVariant::createFromBoolean(true) );
}

The code checks if the BarParentChange ( _EventID == 125) event is fired, and changes the third parameter of the event to true. The definition for BarParentChange event can be consulted in the control's documentation or in the ActiveX explorer. So, anytime you need to access the original parameters for the event you should use the EventParam method that allows you to get or set a parameter. If the parameter is not passed by reference, you can not change the parameter's value. 

Now, let's add some code to see a complex sample, so let's say that we need to prevent moving the bar from an item to any disabled item. So, we need to specify the Cancel parameter as not Items.EnableItem(NewItem), in other words cancels if the new parent is disabled. Shortly the code will be:

// Notifies the application once the control fires an event.
void onEvent_Event(int _EventID)
{
    ;
    if ( _EventID == 125 ) /*event BarParentChange (Item as HITEM, Key as Variant, NewItem as HITEM, Cancel as Boolean) */
        if ( !exg2antt1.Items().EnableItem( exg2antt1.EventParam( 2 /*NewItem*/ ) ) )
            exg2antt1.EventParam( 3 /*Cancel*/, COMVariant::createFromBoolean(true) );
}
In conclusion, anytime the X++ fires the "invalid parameters." while handling an event, you can use and handle the Event notification and EventParam methods of the control.
XPP.8:
The Exontrol ExPrint component is an advanced printing system specifically to bring your User Interface to the printed page. The eXPrint component provides print and print preview capabilities for our UI components.  First, we need to insert the exprint component to the form or dialog. Click the Application Object Tree (AOT), so the AOT window is opened. Open the Forms folder, and then create a new form, or select an existing one. Expands the form being selected, so you can see the Designs item, and locate the Design node. Right-click the Design node for the form, and then select New Control. Select ActiveX from the submenu. Choose the control that you want to insert, in our case, ExPrint ActiveX Control. Click the Properties of the newly ActiveX member inserted, and go to the Name and type exprint1, so you assign a name for your component. Specify the Visible property of the exprint1 on False, so it won't be visible at runtime ( it is a windowless control ).

Now, once the exprint1 component is added to the same form where the control to be printed is hosted ( exprint1.Visible property is false, so the exprint component will not be visible at runtime ), we can use the following function to preview the control:

void callPreview()
{
    anytype var_object;
    ;

    var_object = exg2antt1;
    exprint1.PrintExt(var_object);
    exprint1.Preview();
}

In this sample, the exg2antt1 indicates the component to be printed, so you need to change with the name of the component you would like to preview. 

If the wrong object is being used in the PrintExt method, the environment will display a message in your Infolog such as: "Method 'Preview' in COM object of class 'IExPrint' returned error code 0x80020009 (DISP_E_EXCEPTION) which means: The PrintExt property must be set before calling Preview method." This indicates that you are trying to preview a non-exontrol component, or an exontrol component with no print and print-preview support.  Generally, the eXPrint component provides support for the following components:

XPP.9:
This issue is related to eXG2antt or eXGantt components. Generally, it is caused  by the Chart.PaneWidth property during the init method of the form. At the time, the init method is performed, the control's size is 0 ( so it's width is 0 ), so setting the PaneWidth during the init, will not have any effect, as the specified width should be between 0 and the control's width ( which is 0 at the init method ). In order to fix this, you can handle the activate method of the form and call the PaneWidth during the activate event as follows:
public void activate(boolean _active)
{
    super(_active);

    exg2antt1.Chart().PaneWidth(false,128);
}
You can call the PaneWidth during any form's event, and it has effect only if at the moment of setting, the control's width is not 0.
XPP.10:
This compiler error 74 may occur if calling a sequence of methods that includes parameters in a single line. This could be a limitation of the X++ complier. 

This error can be avoided, if the X++ line is divided in more instructions as in the following samples. Shortly, whenever you use a property that returns an object, just declare a variable of type COM and use the variable next time, as you can see in the following samples:

Here's a situation where the red line indicates the "*** Error 74, The variable is not of the type CLASS." compiler error.

public void init()
{
    ;

    super();

    exg2antt1.Chart().Bars().Item("Task").Color(255);
}

Generally, if a line contains calling several methods with parameters you need to divide it, as follows:

public void init()
{
    COM com_Bars,com_Bar,com_Chart;
    ;

    super();

    com_Chart = exg2antt1.Chart();
        com_Bars = com_Chart.Bars();
            com_Bar = com_Bars.Item("Task");
                com_Bar.Color(255);
}

In most cases you still can use the COM::createFromObject method as in the following samples:

public void init()
{
    COM com_bars;
    ;

    super();

    com_bars = COM::createFromObject(exg2antt1.Chart().Bars());
    COM::createFromObject(com_bars.Item("Task")).Color(255);
}

or

public void init()
{
    COM com_Bar,com_Chart;
    anytype var_Bar,var_Chart;
    ;

    super();

    var_Chart = exg2antt1.Chart(); com_Chart = var_Chart;
        var_Bar = COM::createFromObject(com_Chart.Bars()).Item("Task"); com_Bar = var_Bar;
        com_Bar.Color(255);
}

Another situation, where the red line indicates the "*** Error 74, The variable is not of the type CLASS." compiler error., is:

public void init()
{
    ;

    super();

    exg2antt1.Columns().Add("Task").HeaderBold(True);
}

In order to correct the error, we need to look for the first method that include parameters, so it would be Add. If we check the control's help the Add method of the Columns object returns a Variant, but still contains an object ( a Column object ), so all we need is to convert the return of Add to a COM type as in the following sample:

public void init()
{
    ;

    super();

    COM::createFromVariant(exg2antt1.Columns().Add("Task")).HeaderBold(True);
}