The Infolog

A blog of Dynamics AX development tips and tricks

Skip to: Content | Sidebar | Footer

Using a Custom .net Control within AX 2012

30 September, 2013 (09:43) | Uncategorized | By: Howard Webb

I have developed a form using a 3rd party .net control and used this within a Dynamics Ax form in AX 2012. The control I used was the Amazing progress bar found here:

http://www.codeproject.com/Articles/182973/The-Amazing-ProgressBar-Control

To use a custom .net control firstly you will need to add the reference DLL file to all client.

Adding the DLL

Firstly move the DLL file in to the bin folder of your client (normally C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin). Then within a Dynamics Ax development workspace right click on the reference node in the AOT and select “Add reference”:

 

In the new screen select browse and then select your DLL. Select your Assembly and click ok. You should now see your reference in the references node:

 

 

Adding the control to a form

We will be adding the amazing progress bar to a simple form, however I have also added a .net button and progress bar.

To add the control right click on the design and select a new managed host control:

 

 

 

In the next popup you should see your managed reference:

 

I then added the other controls:

Make sure that the managed host is auto declared as you will need to set the properties in code.

 

Add your controls to the class

public
class FormRun extends ObjectRun

{

 

System.Windows.Forms.Button managedButton;

System.Windows.Forms.ProgressBar managedProgressBar;

GAW.AmazingProgressBar _a;

}

 

Within the init of your form you can then set the properties of the managed control:

 

public
void init()

{


int i;

System.Drawing.Color colour;

System.Drawing.Graphics g;

 

 

;

 


super();

 

 


try

{


//Button settings

managedButton = ManagedHost1.control();

managedButton.set_Text(“hello”);

managedButton.add_Click(new ManagedEventHandler(this, ‘managedButtonclicked’));

 

managedProgressBar = ProgressBar.control();

 


//progress bar settings

 

managedProgressBar.set_Minimum(1);

managedProgressBar.set_Maximum(100);

 

_a = amazing.control();

_a.set_MazeStyleInt(0);

_a.set_RowCount(6);

_a.set_Width(400);

_a.set_Height(50);

_a.set_ForeColor(colour);

_a.Update();

_a.Refresh();

amazing.update();

}


catch(Exception::CLRError)

info(strFmt(AifUtil::getClrErrorMessage()));

 

}

 

And then on the method for the button click :

 

public
void managedButtonclicked(System.Object sender, System.EventArgs e)

{

 


int i;

;

 

 


for(i = 1; i <= 100; i++)

{

managedProgressBar.set_Value(i);

_a.set_Value(i);

_a.Update();

amazing.update();


sleep(30);

}

 

}

 

When run it will set the value and run the progress bar:

 

 

Further reading:

http://dynamicsaxgyan.wordpress.com/2011/12/10/managedhost-control-in-dynamics-ax-2012/

 

 

Adding a chart to an SSRS report

30 September, 2013 (09:26) | Uncategorized | By: Howard Webb

I have added a graph to the top 100 customers report in AX2012 and it already has the percentage field on the report. I had thought that it would be just a case of referencing that field and then creating a chart. While this might be possible in the end I modified the DP class to do the calculation AX side.

 

Firstly you will need to open the report design in Visual studio:

 

I have decided to add this graph in the body of the report. In the tool box we now have the graph tool:

 

When you place the object you will be greeted by the wizard, where you can select your chart type.

 

 

After sizing the graph area you have 3 areas to set the values used in the graph

 

To the top you have the fields that are used to calculate the values i.e. the y values in a bar chart. Please note that you can use aggregate functions and calculations. If you right click on the grey box around the data field you can select series properties and that brings up the advanced properties including the expression builder.

 

The category fields are the grouping of the data, i.e. the x values in a bar chart. The series fields would be subgroups within the category.

The individual elements that build the chart are controlled as per other elements in SSRS reports and can be moved around:

 

 

They can also have all of the standard hidden expressions etc.

Saving and merging Infolog items for later use

29 September, 2013 (15:38) | Uncategorized | By: Howard Webb

The job below that will get Infolog items, clear them and save each one for later use. This can be of use when you need to record the Infolog for later viewing

static
void Job1(Args _args)

{

container errors;

container t;

int i;

int length;

;

 

warning(“Test1”);

error(“Test2”);

errors = conIns(errors, 1, t);

 
 

info(“extra”);

 
 

t = infolog.cut();

 
 

errors = conIns(errors, 2, t);

length = conLen(errors);

 
 

for( i =1; i <= length; i++)

{

t = conPeek(errors, i);

infolog.import(t);

}

 
 

}

Using the Outlook API to produce drafts

29 September, 2013 (15:22) | Uncategorized | By: Howard Webb

I have been playing a bit with the email process of an SSRS report in AX 2012 as a self-learning exercise. Thought it might be worthwhile to share the code I have written so far. Using real life examples of requested functionality I have changed the SalesInvoice report to:

 

  • Create an Email and save it to the drafts folder of Outlook
  • Create the invoice as a secured PDF
  • Add an additional attachment to the email

 

To do this I made a change to \Classes\SrsReportRunPrinter\toEmail\:

 


 

And then here is my Method:

 

static
public
void EncryptPDF(  FilePath                    _PDFLoc,

                                SrsReportEMailDataContract  _emailContract)

{

   
 

    
 

    
 

    Microsoft.Office.Interop.Outlook._Application                                           outlookApp;

    Microsoft.Office.Interop.Outlook.ItemsClass                                             itemsClass;

    Microsoft.Office.Interop.Outlook.Attachment                                             attachment;

    Microsoft.Office.Interop.Outlook.Attachments                                            attachments;

    Microsoft.Office.Interop.Outlook.MailItemClass                                          mailItemClass;

    Microsoft.Office.Interop.Outlook.NameSpaceClass                                         Nspace;

    Microsoft.Office.Interop.Outlook.MAPIFolder                                             mapiFolder;

    int                                                                                     numEmails;

    int                                                                                     i;

    System.String                                                                           dotNetString;

    str                                                                                     xPlusPlusString;

    Microsoft.Office.Interop.Outlook.MailItem                                               message;

    Microsoft.Office.Interop.Outlook.AddressEntry                                           sender;

    Microsoft.Office.Interop.Outlook.Items                                                  item;

   
 

    FilePath    GSDir;

    Filename    GS;

    Filename    PDFName;

    FilePath    TMPDir;

    FilePath    outputDir;

    str         param;

    str         param1;

    str         param2;

   
 

    Filename    xyz = System.IO.Path::GetFileName(_PDFLoc);

    str         newFileNameX;

    ;

 

 

    new InteropPermission(InteropKind::ClrInterop).assert();

    outlookApp  = new Microsoft.Office.Interop.Outlook.ApplicationClass();

    Nspace      = outlookApp.GetNamespace(‘Mapi’);

    //Set values     

    outputdir   = “C:\\temp\\Secure\\”;

    TMPDir      = “C:\\temp\\UnSecure\\”;

   
 

    GS          = “gswin64.exe”;

    GSDir       = “C:\\Program Files\\gs\\gs9.02\\bin\\”;

    param1      = “-dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOwnerPassword=password1 -dKeyLength=128 -dEncryptionR=3 -dPermissions=-3904 -sOutputFile=C:\\temp\\Secure\\”;

   
 

    newFileNamex = TMPDir + xyz;   

    
 

    //first pickup PDF and move it to TMP   

    try

    {

        WinAPI::moveFile(_PDFLoc, newFileNamex);

    }

   
 

    catch(Exception::CLRError)

        info(strFmt(AifUtil::getClrErrorMessage()));

    //Create Param

   
 

    param = param1 + xyz + ” “ + TMPDir + xyz;

    info(param);

   
 

    
 

    
 

    //With file at known loc call GS

   
 

    WinAPI::shellExecute((GSDir + GS), param);

   
 

    //now send email with attachment

   
 

    try

    {

        mapiFolder  = Nspace.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders::olFolderDrafts);

        itemsClass  = mapifolder.get_Items();

        message     = outlookapp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType::olMailItem);

        message.set_To(_emailContract.parmTo());

        message.set_Subject(_emailContract.parmSubject());

        message.set_Body(_emailContract.parmBody());

        Message.set_CC(_emailContract.parmCc());

        //Message.set_SentOnBehalfOfName(“dkos”);

        attachments = message.get_Attachments();

        attachments.Add(@outputdir + xyz, Microsoft.Office.Interop.Outlook.OlAttachmentType::olByValue, 1, “”);

        attachments.Add(@”C:\\temp\\HWEB.pdf”, Microsoft.Office.Interop.Outlook.OlAttachmentType::olByValue, 2, “”);

        Message.Save();

    }

    catch(Exception::CLRError)

        info(strFmt(AifUtil::getClrErrorMessage()));  

}

 

 

 

It’s a pretty rough and ready example and this is no more than a quick POC but you get the idea.

Navigating XML with X++

29 September, 2013 (13:30) | Dynamics AX | By: Howard Webb

About a year ago I had the luxury of some self learning time at work. I set about a task to intergrate a mapping utility with route planning. I chose Google maps as it returned an XML file that was easy to read.

I wanted to pick up the total distance and time for my Google maps mod. You can use Google’s API to get an XML file returned by passing it a dynamic URL (an example is attached). Within that you have the total distance and time and the status of the request (for use with exception handling). Most of the AX books I have do not have great examples of getting values from an XML file. They seem to concentrate on alliterating through a few child nodes to populate a table and not getting values directly. They use code lines such as:

Doc.selectNodes(‘//’+tablestr(LedgerTable));

I did a Google for examples of picking up a single value from within an XML file and could not find too much so I looked in to it. All examples I have found use the following to select a node from within an XML file:

Doc.selectSingleNode(‘//NodeName’);

However I found that this will only work if the node name is unique as it will select the first occurrence of the node name if not. I had thought this to be an issue and that AX should respect where in the XML file you were operating. Not knowing better I wrote a few lines of code to loop through the entire XML file to get the node I actually wanted. However a co-worker kindly pointed me down the path towards an XPath solution.

For those not within the know XPath is a query language used by lots of programs to allow selecting of areas/nodes from within an XML file. If you’ve ever done anything with XML files within AX you’ll have noticed something like this:

xpath

 

If you look at the W3C webpage the method was performing as expected, a double / before a node name should “Select nodes in the document from the current node that match the selection no matter where they are” and using single node was just returning the first example it found. The solution to the problem was to use the full XPath. After a few issues I have discovered that you need the whole path and do not need a double slash.

As an example in that file the line:

info(doc.selectSingleNode(‘/route/leg/distance/text’).text());

Will return “288 km” – the total distance for the journey

More can be found on Xpath here:

http://en.wikipedia.org/wiki/XPath

http://www.w3schools.com/xpath/xpath_syntax.asp

and I’d suggest that you use the XML tools plugin for NotePad ++ which can pick up the XPath for you from a file.

 

Adding to a container with += vs. ConIns()

29 September, 2013 (01:25) | Dynamics AX | By: Howard Webb

While adding data to the end of a container there can be large savings by using += rather than ConIns. Try the code below and compare the results:

 

static void Containertest(Args _args)

{

int                            ticks;

Container                con;

Random                  random = new Random();

int                            myrandom;

int                            i;

;

 

//Start on ins

ticks = WinApi::getTickCount();

for(i = 1; i <= 100000; i++)

{

myrandom = random.nextInt();

con = conins(con, i, myrandom);

}

ticks = WinAPI::getTickCount() – ticks;

info(strfmt(“Time taken via ConIns: %1”, ticks));

 

//Start on +=

ticks = WinApi::getTickCount();

for(i = 1; i <= 100000; i++)

{

myrandom = random.nextInt();

con += myrandom;

}

ticks = WinAPI::getTickCount() – ticks;

info(strfmt(“Time taken via +=: %1”, ticks));

}

Using a query with SysLookupMultiSelectCtrl

29 September, 2013 (01:21) | Dynamics AX | By: Howard Webb

If you wish to use the new SysLookupMultiSelectCtrl class there are a few construct methods out there that you can use. If you need to build one using a dynamic query object you can use this construct method, however the problem with this is that the lookup will contain all the fields in your table. To avoid this you need to remove the fields from the field list and then put in the fields you want:

 

public void dialogPostRun(DialogRunbase _dialog)

{

Query                   query = new Query();

QueryBuildDataSource    queryBuildDataSource = query.addDataSource(tablenum(AssetBook));

FormRun                 formRun;

;

 

super(_dialog);

 

formRun = _dialog.dialogForm().formRun();

queryBuildDataSource.addGroupByField(fieldnum(AssetBook, BookId));

queryBuildDataSource.fields().clearFieldList();

queryBuildDataSource.fields().addField(fieldNum(AssetBook, BookId));

 

fsCtrlMultiSelect1 = formRun.design().control(fbsCtrlMultiSelect1.id());

 

msCtrl1 = SysLookupMultiSelectCtrl::constructWithQuery(formRun, fsCtrlMultiSelect1, query);

}

Formatting on fields within SSRS

29 September, 2013 (01:18) | Uncategorized | By: Howard Webb

SSRS pulls the formatting direct from the field’s EDT in AX, however this only works if the field is on the report table and is not a aggregate function. As such it does not really work for values that will be used in a header or footer section. There are plenty of examples of this causing issues in standard reports, for example the invoice date on a sales invoice (which is displayed as a UTC not a date),  and as such it is not really a valid way to output to the user. While you can apply formatting within the SSRS report, it will only apply the date format on the SSRS server. To avoid this issue I have started to use a string rather than the source field’s EDT. You can then use strfmt or one of the other type2str methods in system to make sure you get a correct value that matches the EDT and also the regional settings for the user.

Accessing and setting the SSRS print destinations

29 September, 2013 (00:57) | Uncategorized | By: Howard Webb

The SSRS print destinations are stored on the report’s contract and can be accessed and set with the following code on the controller:

controller.parmReportContract().parmPrintSettings();