Dynamics AX has a standard window control which you can display images or draw simple figures and bitmaps using WinGDI class of the Dynamics AX. Those class methods use standard low level Windows GDI drawing functions and do not support many of the features came with today’s .NET drawing libraries including alpha blending and some 2D and 3D drawing libraries. What if you would like to use all .NET features on AX window control instead of sticking with simple WinGDI class functions?

From on AX2012, this is officially done using managed host control in AX form instead of the window control (See my other article about using managedhost control). What I show in this article is another way to draw directly on the form window control, in case the managedhost does not work for you or you use lower versions of AX than 2012. It is possible to convert managed .NET bitmaps to GDI useable bitmap pointers and draw them on a GDI Device Context (DC) provided by AX window control using legacy windows BitBlt function. Let’s do a simple example and see how it works.

First we create a new form and add a window control and set the properties as seen below

capture1

We set the color scheme to RGB and set all colors to default white one by default. The other settings helps us use the window control as a drawing context instead of being treated a normal standard AX control.

Now we need to import some windows DLLS into our code and create a .NET image buffer to perform to drawing (instead of drawing directly on the DC of windows control. We import the DLLs using AX DLL and DLLFunction classes. We declare them in our class declaration :

public class FormRun extends ObjectRun
{
 System.Drawing.Bitmap buffer;
 DLL DLLGDI;
 DLLFunction dllcreatecompdc;
 DLLFunction dllselectObject;
 DLLFunction dlldeleteObject;

}

and create a new initDLL method which will be called in our form’s init method :

private void initDLL()
{
 interopPermission interopPermission = new InteropPermission(InteropKind::DllInterop);
 ;
 interopPermission.assert();

 //Initialize DLL functions
 DLLGDI = new DLL('GDI32');
 dllcreatecompdc = new DLLFunction(DLLGDI, 'CreateCompatibleDC');
 dllcreatecompdc.returns(ExtTypes::DWord);
 dllcreatecompdc.arg(ExtTypes::DWord);
 dllselectObject = new DLLFunction(DLLGDI, 'SelectObject');
 dllselectObject.returns(ExtTypes::DWord);
 dllselectObject.arg(ExtTypes::DWord,ExtTypes::DWord);
 dlldeleteObject = new DLLFunction(DLLGDI, 'DeleteObject');
 dlldeleteObject.returns(ExtTypes::DWord);
 dlldeleteObject.arg(ExtTypes::DWord);</pre>
<pre>CodeAccessPermission::revertAssert();</pre>
<pre>}

init method :

public void init()
{
 super();</pre>
this.initDLL();
}

Then we need to initialize our drawing buffer with the width and height of our window:

public void run()
{
 int w = Wnd.widthValue();
 int h = Wnd.heightValue();
 interopPermission clrinteropPermission = new InteropPermission(InteropKind::ClrInterop);
 clrinteropPermission.assert();

 super();
buffer = new System.Drawing.Bitmap(w, h);</pre>
<pre>CodeAccessPermission::revertAssert();</pre>
<pre>}

I choose run method to initialize it with its width and height after the form is drawn. For now, not to make it more complicated, I do not add any controls for resizing the form etc., so if you want to resize the drawing area, you need resize the form first and restart the form with its saved size. After you have learned the basics you can extend it as much as you want for your purpose.

Now lets write the method that will draw our .NET buffer into our code and explain it part by part :

public void draw()
{
 #wingdi

 int i,j;
 System.IntPtr hptr;
 WinGDI gdi;
 System.Drawing.Graphics netWnd;
 Set permissionSet;
 ;

 permissionSet = new Set(Types::Class);
 permissionSet.add(new InteropPermission(InteropKind::DllInterop));
 permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
 CodeAccessPermission::assertMultiple(permissionSet);

 Wnd.lockDC();
 gdi = new WinGDI(Wnd.hDC());

 hptr = buffer.GetHbitmap();
 i = hptr.ToInt32();

 j = dllcreatecompdc.call(Wnd.hDC());

 dllselectobject.call(j, i);

 gdi.bitBlt(0, 0, buffer.get_Width(), buffer.get_Height(), j, 0, 0, #SRCCOPY);

 Wnd.unlockDC();

 dlldeleteobject.call(j);
 dlldeleteobject.call(i);

 CodeAccessPermission::revertAssert();
}

Here we juggle in between legacy GDI32.dll functions, AX WinGDI class functions and .NET ones. First we lock our drawing context to prevent other paint functions draw on it in the form. Then we get our GDI bitmap object with the HBitmap function. Converting this value to an integer returns us a pointer value to our bitmap. We assign it to an integer variable and then we create our GDI DC using dllcreatecompdc method with hDC() value of the window control and assign it to another pointer variable. Later the dllselectobject function helps us to draw that bitmap into our AX window control. We use the legacy bitBlt function to transfer or bitmap into our window control. Lastly we unlock our DC and clean the GDI objects we have created.

Now we need to call this draw method from the overloaded paint method of our windows control. To avoid flickers, we remove the super() call which paints the window control with background color.

public int paint()
{
int ret;

//ret = super();
element.draw();

return ret;
}

That’s all we need to draw a .NET bitmap to an AX window control. To see it in action we need to draw something on our .NET bitmap and update the window. Let’s create a simple example which draws points on our window with mouse drag. For this purpose, we override the mousemove method of the window control and draw points on our bitmap when our left button is pressed :

public int mouseMove(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift)
{
int ret;
System.Drawing.Graphics graphics;
interopPermission clrinteropPermission = new InteropPermission(InteropKind::ClrInterop);
;

ret = super(_x, _y, _button, _Ctrl, _Shift);

if(_button == 1)
{
clrinteropPermission.assert();

graphics = System.Drawing.Graphics::FromImage(buffer);

graphics.FillEllipse(System.Drawing.Brushes::get_Black(), _x, _y, 4, 4);

graphics.Dispose();

Wnd.paint();

CodeAccessPermission::revertAssert();
}

return ret;
}

We call the paint function manually here to transfer our .NET bitmap to the windows control. As you see here it draws on our control without any flickering and because the bitmap is hold in the buffer, it draws it without any delays if we minimize and maximize and resize our form.

Capture2.PNG

Do not forget to dispose the graphics on form closing :

public void close()
{
interopPermission clrinteropPermission = new InteropPermission(InteropKind::ClrInterop);
clrinteropPermission.assert();

super();

buffer.Dispose();

CodeAccessPermission::revertAssert();

}

This is just a simple example. From here on you can extend it to use advanced .NET drawing functions and alpha blending to create advanced graphics just inside an AX form window control.

You can download XPO for this form example from my GitHub :

Form_formDrawTest.xpo

Enjoy!

 

Leave a Reply