Creating a run-time lookup form

Posted: April 8, 2009 in AX
 

A standard lookup form is created through relations on the database table, and on the Extended Data Type. To learn about the standard lookup form, click .

If, however, you need to create a runtime lookup form that looks up other database fields than the ones offered by the standard lookup form, use the application class SysTableLookup and override the lookup method on the relevant form control.

How to use the SysTableLookup class

 

  1. Create a new instance of SysTableLookup where ‘this’ is the current form control

   SysTableLookup    sysTableLookup = SysTableLookup::newParameters(tableNum(custTable), this);

  1. Add the fields to be shown in the lookup form

    sysTableLookup.addLookupField(fieldNum(custTable, accountNum));

  1. Limit the data selection.

    queryBuildDataSource = query.addDataSource(tableNum(custTable));

    queryBuildRange      = queryBuildDataSource.addRange(fieldNum(custTable, accountNum));

    queryBuildRange.value(‘A..B’);

    sysTableLookup.parmQuery(query);

  1. Perform the lookup, and delete super().
    super() will create an autogenerated lookup form.

    sysTableLookup.performFormLookup();

//    super()

}

 

A complete example of overriding the lookup method on a form control

 

void lookup()

{

    Query                   query          = new Query();

    QueryBuildDataSource    queryBuildDataSource;

    QueryBuildRange         queryBuildRange;

 

// Create an instance of SysTableLookup where ‘this’ the current Form control.

 

    SysTableLookup          sysTableLookup = SysTableLookup::newParameters(tableNum(custTable), this);

    ;

 

// The field to be shown in the lookup form.

 

    sysTableLookup.addLookupField(fieldNum(custTable, accountNum));

    sysTableLookup.addLookupField(fieldNum(custTable, name));

 

// Limit and arrange data selection.

 

    queryBuildDataSource = query.addDataSource(tableNum(custTable));

    queryBuildRange      = queryBuildDataSource.addRange(fieldNum(custTable, accountNum));

    queryBuildRange.value(‘A..B’);

    sysTableLookup.parmQuery(query);

 

// Perform lookup

 

    sysTableLookup.performFormLookup();

 

// do not call super().

 

//    super()

}

 

 

 

 

 

void lookup()

{

  Query query = new Query();

  QueryBuildDataSource queryBuildDataSource;

  QueryBuildRange queryBuildRange;

  SysTableLookup sysTableLookup;

  TableId tableId;

  FieldId fieldId;

  ;

  tableId = tablename2id(‘myTable’);

  sysTableLookup.parmTableId(tableId);

  fieldId = fieldname2id(tableId, ‘MyField_1’);

  sysTableLookup.addLookupfield(fieldId);

  fieldId = fieldname2id(tableId, ‘MyField_2’);

  sysTableLookup.addLookupfield(fieldId);

 

  queryBuildDataSource = query.addDataSource(tableId);

  queryBuildDataSource.orderMode(OrderMode::GROUPBY);

  queryBuildDataSource.addSortField(fieldId));

 

  sysTableLookup.parmQuery(query);

 

  this.performFormLookup(sysTableLookup.formRun());

}

This manner of creating a lookup form is used in the DocuType form in the application. The full path to the modified lookup method is:

\Forms\DocuType\Designs\Design\[Tab:Tab]\[TabPage:Overview]\[Grid:Grid]\StringEdit:ActionClassName\Methods.

Note

The SysTableLookup is limited to making lookups on fields in a table.

Functions of Container

Posted: February 27, 2009 in AX

FROM AX DOCUMENT

ConIns

Syntax 

container ConIns (container container, 
             int start, 
             anytype element,... ) 

Exception message 

Inserts elements in the container specified by container. The elements are inserted at the position specified by start. If several elements are specified, they must be separated by commas. To insert an element as the first in a container, specify start as 1. To insert after the nth element specify start as n+1.

Example 

static void Job_Examples_conins_conlen_conpeek(Args _args)
{
  container c;
  int i;
  ;

  c = conins(["item1", "item2"], 1);  // conins to insert into container
  for (i = 1 ; i <= conlen(c) ; i++)  // conlen for the size of the container
  {
    print conpeek(c, i);              // conpeek to get contents of container item without removing it
  }

  pause;
}

 

Notes

You can also assign elements to a container:
container c = ["Hello World", 1, 3.14];

You can use the += operator to add values of any type into a container. For example, to create a container containing the first ten square numbers:
int i;
container c;
;

for (i = 1; i < = 10; i++)
{
    c += i+i;
}

See Also 

ConFind, ConDel, ConLen, ConNull, ConPeek, ConPoke

ConFind

Syntax 

int ConFind (container container, anytype element,... ) 

Exception message 

Locates the first occurrence of a specific sequence of elements in the container specified by container. If several elements are specified in the sequence, they must be separated by commas and given in the correct sequence. The elements can be of any data type.

Return Value 

The function returns 0 (the item was not found) or the item’s sequence number. 

Example 

static void Job_Examples_confind(Args _args)
{
  container c = ["item1", "item2", "item3"];
  int i;
  ;
 
  i = confind(c, "item2");
  print @"Position of 'item3' in container is " + int2str(i);
  pause;
}

See Also 

ConDel , ConIns , ConLen , ConNull , ConPeek , ConPoke

 

ConLen

Syntax

[HEADER]

Exception message  

Returns the number of elements in the container specified by container.

Example 

static void Job_Examples_conins_conlen_conpeek(Args _args)
{
  container c;
  int i;
  ;

  c = conins(["item1", "item2"], 1);  // conins to insert into container
  for (i = 1 ; i <= conlen(c) ; i++)  // conlen for the size of the container
  {
    print conpeek(c, i);              // conpeek to get contents of container item without removing it
  }

  pause;
}

See Also

ConFind, ConDel, ConIns, ConNull, ConPeek, ConPoke

 

ConNull

Syntax 

[HEADER]

Exception message 

Returns a null container. Use the function to explicitly dispose of the contents of a container.

Example 

static void Job_Examples_connull(Args _args)
{
  container c = ["item1", "item2", "item3"];
  ;

  print "Size of container is " + int2str(conlen(c));
  c = connull();  // set container to null
  print "Size of container after connull() is " + int2str(conlen(c));
  pause;
}

See Also 

ConFind, ConDel, ConIns, ConLen, ConPeek, ConPoke

 

ConPeek

Syntax 

[HEADER]

Exception message 

Returns a specific element from the container specified by container. The element is specified by number. Specify 1 to get the first element.

The conpeek function automatically converts the peeked item into the expected return type. Strings can automatically be converted into integers and reals, and vice versa.

Example

static void Job_Examples_conins_conlen_conpeek(Args _args)
{
  container c;
  int i;
  ;

  c = conins(["item1", "item2"], 1);  // conins to insert into container
  for (i = 1 ; i <= conlen(c) ; i++)  // conlen for the size of the container
  {
    print conpeek(c, i);              // conpeek to get contents of container item without removing it
  }

  pause;
}

Note 

You can extract more than one element from a container with a composite assignment:
void myMethod()
{
    str 10 s;
    int i;
    real r;
    container c = [1,3.14,"Hello"];
    ;

    // Extracts 1 into i, 3.14 into r and "Hello" into s
    [i,r,s] = c;
}

See Also 

ConFind, ConDel, ConIns, ConLen, ConNull, ConPoke

 

ConPoke

Syntax 

container ConPoke (container container, 
                  int start, 
                  anytype element,... ) 

Exception message 

Modifies the container specified by container by replacing one or more of the existing elements with the specified elements. The position of the first element to be replaced is specified by start. If several elements are specified, they must be separated by commas. To replace the first element, specify start as 1.

Example 

static void Job_Examples_conpoke(Args _args)
{
  container c1 = ["item1", "item2", "item3"];
  container c2;
  int i;
  void conPrint(container c)
  {
      for (i = 1 ; i <= conlen(c) ; i++)
      {
        print conpeek(c, i);
      }
  }
  ;
  conPrint(c1);
  c2 = conpoke(c1, 2, "PokedItem");
  print "";
  conPrint(c2);
  pause;
}

ConFind , ConDel , ConIns , ConLen , ConNull , ConPeek

 

Starting the code profiler from code

Posted: March 26, 2008 in AX

original link: http://axstart.spaces.live.com/

 

The code profiler is a tool that tells you how many milliseconds a line of x++ code need to be executed. This tool is most of time used for analyses of defined scenario of your process. This code profiler runs the code in debug mode. So a process of 10 minutes can take 30 minutes when the code profiler is active. 

So a large run of 1 hour, reproduce with the code profiler, makes no since. The trick is to profile small part of the process. We can do this with existing macros in AX.

#profileBegin(‘myrun’)

//mycode

#profileEnd

 

Later on you have to calculate the sum and line totals for that run. Give this run a new name, otherwise you don’t get the line, method and class totals. I think this is a bug.

Send Mail through code

Posted: March 14, 2008 in AX
AX uses CDO.Message component to send a email and Here’s a example.
 
static void SendMailQuick(Args _args)
{
    SysMailer   mailer;
    ;
   
    mailer = new SysMailer();
   
    mailer.quickSend("david@dickiesmedical.com", "david@dickiesmedical.com", "test subject", "test body", "sjin@dickiesmedical.com", "c:\\testfile.txt");
}
 
 
or other way
 
 before using this way you need to setup email parameters through Administration – Setup – E-mail parameters
static void sendMail(Args _args)
{
    #SysMailer
    #DEFINE.SenderName(‘David Park’)
    #DEFINE.Sender(‘david@dickiesmedical’)
    #DEFINE.Recipient(‘david@dickiesmedical.com’)
    #DEFINE.Recipient2(‘sjin@dickiesmedical.com’)
    #DEFINE.Subject(‘test mail subject’)
    #DEFINE.MessageBody(‘test mail message body’)
   
    SysMailer   mailer;
    SysMailerAddressField   tos;
   
    FileIOPermission fileIOPermission;
    InteropPermission interopPermission;
   
    str relayServer;
    int portNumber;
    str userName;
    str password;
    boolean NTLM;
   
    str filePathName;
    str tempPath;
   
    SysEmailParameters parameters = SysEmailParameters::find();
    ;
   
    if (parameters.SMTPRelayServerName)
        relayServer = parameters.SMTPRelayServerName;
    else
        relayServer = parameters.SMTPServerIPAddress;
       
    portNumber = parameters.SMTPPortNumber;
    userName = parameters.SMTPUserName;
    password = SysEmailParameters::password();
    NTLM = parameters.NTLM;
   
    //fileIOPermission = new FileIOPermission(”,’r’);
    //fileIOPermission.assert();
   
    //tempPath = WinAPIServer::getTempPath();
   
    //CodeAccessPermission::revertAssert();
   
    interopPermission = new InteropPermission(InteropKind::ComInterop);
    interopPermission.assert();
    mailer = new SysMailer();
    CodeAccessPermission::revertAssert();
   
    mailer.SMTPRelayServer(relayServer, portNumber, userName, password, NTLM);
   
    mailer.fromAddress(#Sender,#SenderName);
 
    tos = mailer.tos();
    //tos.appendAddress("david@dickiesmedical.com,sjin@dickiesmedical.com");
    tos.appendAddress(#Recipient);
 
    mailer.priority(1);
    mailer.subject(#Subject);
    mailer.htmlBody(#MessageBody);
   
    mailer.sendMail();
}
 
Or
 
If you are up and running E-mail distributor batch, then simple insert apropriate data into SysOutgoingEmailTable and SysOutgoingEmailData(Optional). batch will get the record from the table and try to send out.
 

Create Progress Indicators

Posted: March 12, 2008 in AX

Use a progress indicator during operations that take more than 2 seconds.

  • Use an hourglass mouse pointer if the process takes 2-7 seconds.
  • Use a progress bar if the process takes 8 seconds or more.

Display an Hourglass Pointer

  1. Call the startLengthyOperation method when the operation starts.
  2. Call the endLengthyOperation method when the operation ends.

Both of these methods are on the Global application class.

The hourglass pointer is displayed until you call the endLengthyOperation method.

Note

You do not have to declare a variable when you use the Global class.

Example: Display an Hourglass Pointer

The following example overrides the clicked method on a form control to refresh the database log.

void clicked()

{

    super();

    startLengthyOperation();

    sysDatabaseLog_ds.research();

    endLengthyOperation();

}

Display a Progress Bar

  1. Initialize a SysOperationProgress variable.
  2. Set a caption for the form by using the SysOperationProgress.setCaption method.
  3. Set an animation to run while the operation is in progress by using the SysOperationProgress.setAnimation method.

    A number of animations are provided with Microsoft Dynamics AX. To view them, run the Tutorial_ShowAVIFiles class. If you use one of these animation files, you need to declare the AviFiles macro at the top of your code.

  4. Specify the total number of operation steps.

    This is needed for the time-remaining calculation. If you do not set the total number of operation steps, the progress indicator is not shown. The total is often a count of the number of records, and may be time-consuming to calculate. Don’t specify the total if the time is taken to calculate the records is comparable to the total time taken for the operation.

  5. Perform the operation. For each step, specify a description and a step number. For example:

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

    {

        progress.setText(strfmt("Step %1", i));

        progress.incCount();

    }

    The description must be short and informative because it might change quickly during the execution.

    As an alternative to incCount() you can use setCount(int i).

During the execution, the progress indicator is updated accordingly. The estimated time remaining is calculated and displayed.

The default update interval is 3 seconds. If the task of updating the display takes more than 10% of the update interval due to latency on the network connection, the update interval is increased by 1 second.

Tip

Separate an operation into as many steps as possible. This gives the user the best information and the best remaining-time estimate. The time spent informing the user has no impact because the form is updated only once a second—even less frequently on low bandwidth connections.

Example: Use a Single Progress Indicator

The following example creates a simple progress indicator.

static void operationProgress(Args _args)
{
    #AviFiles
    SysOperationProgress progress = new SysOperationProgress();
    int i;

    ;

    progress.setCaption("My Task");
    progress.setAnimation(#AviUpdate);
    progress.setTotal(30000);
    for (i = 1; i <= 30000; i++)
    {
        progress.setText(strfmt("Step %1", i));
        progress.setCount(i, 1);
    }
}

Use More Than One Progress Indicator

If you have a more complex operation, you can have more than one progress indicator. All of the previously described methods have a default parameter that indicates which progress indicator (or bar) is being referred to.

Note

The time-remaining calculation is done solely on the first bar. The first bar must always show the overall progress.

Example: Use Three Progress Indicators

static void SysOperationProgress(Args _args)
{
    #define.count1(10)
    #define.count2(5)
    #define.count3(200)
    #AviFiles
    
    // 3 bars.
    SysOperationProgress progress = new SysOperationProgress(3);
    int i, j, k;
    ;
 
    progress.setCaption("My Task");
    progress.setAnimation(#AviUpdate);
 
    // Bar 1.
    progress.setTotal(#count1, 1);
    // Bar 2.
    progress.setTotal(#count2, 2);
    // Bar 3.
    progress.setTotal(#count3, 3);
 
    for (i=0; i<#count1; i++)
    {
        // Bar 1.
        progress.setText(strfmt("Bar 1 - Step %1 of %2", i, #count1), 1);
        progress.setCount(i, 1);
        for (j=0; j<#count2; j++)
        {
            // Bar 2.
            progress.setText(strfmt("Bar 2 - Step %1 of %2", j, #count2), 2);
            progress.setCount(j, 2);
            for (k=0; k<#count3; k++)
            {
                // Bar 3.
                progress.setText(
                    strfmt("Bar 3 - Step %1 of %2", k, #count3), 3);
                progress.setCount(k, 3);
                sleep(20);      // Time-consuming task.
            }
        }
    }
}

User Input During an Operation

Following are progress indicator options for user input during an operation:

  • SysOperationProgress.hide – hides the progress form and pauses the time-remaining calculation. The progress indicator reappears when setCount, incCount, or setText is subsequently called.
  • SysOperationProgress.kill – terminates the progress form and starts the progress again. You don’t have to reset the caption, animation, and total.

Using Progress Indicators with the Runbase Framework

Runbase is the standard framework for job execution, and must have a progress indicator. In the standard application, most progress indicators are used from the RunBase framework.

Use the RunBase.progressInit method to initialize the progress indicator:

public void progressInit(

    str caption,

    int total,

    Filename animation,

    int updateInterval = 1,

    int numOfBars = 1)

Indicating progress during the actual operation is similar to the standard Operation Progress framework. Use the member variable progress:

progress.incCount();

If you have more than one progress bar, use the progress variable defined in RunBase. It points to the Progress class initialized in the progressInit method. For example:

{
    this.progressInit("Caption",10,#AviFileCopy,1,2);
    progress.setCount(bar1count);
    progress.setTotal(maxBar2Count,bar2count);
    ...
    ...
    progress.setCount(bar2count,2);
}

To unnecessary code to avoid duplications, we can even with a for-loop work:

void clicked() Void clicked ()
{ (
CustTable custTable; CustTable custTable;
;
for (custTable = CustTable_ds.getFirst(true) ? For (custTable = CustTable_ds.getFirst (true)?
CustTable_ds.getFirst(true) : CustTable_ds.getFirst (True):
CustTable_ds.cursor(); CustTable_ds.cursor ();
custTable; CustTable;
custTable = CustTable_ds.getNext()) CustTable = CustTable_ds.getNext ())
{ (
//do something with custTable / / Do something with custTable
info(custTable.accountNum); Info (custTable.accountNum);
} )
} )

CustTable_ds.getFirst() gibt nur etwas zurück, wenn mehrere Datensätze markiert sind. CustTable_ds.getFirst () is only slightly back when several records are marked. Man kann sich also die Abfrage nach CustTable_ds.anyMarked() sparen. It is also the query () CustTable_ds.anyMarked save.

how to init the form in the maximized mode?

Posted: February 18, 2008 in AX
I think it depends on design properties of your form.

My form has following properties
Width – Column width
Height – Column height

WindowResize – Dynamic

and move code I suggested in run() method

Ruslan Goncharov

http://rusgon.blogspot.com/

"M Elsawah" wrote:

> Hi Ruslan
>
> Thanks alot for your help
> but
> It works but over the width only, i want the form to look like the action of
> clicking on Maximize button, have you got what i mean?
>
> Thanks.
>
> "Ruslan Goncharov" wrote:
>
> > Try to do following lines in the init() method on the form
> > public void init()
> > {
> >     #WinApi
> >     ;
> >     super();
> >
> >     WinApi::showWindow(this.hWnd(), #SW_SHOWMAXIMIZED);
> > }
> > —
> > Ruslan Goncharov
> >
> > http://rusgon.blogspot.com/
> >
> >
> > "M Elsawah" wrote:
> >
> > > Hi all,
> > > I just ask about how to make the form open in the maximized mode in both
> > > width and height? also i want to ask how to make the controls resized
> > > relative to the form size?
> > > Please try to reply me ASAP
> > > Thanks.

save and open word document

Posted: January 31, 2008 in AX
static void WordTemplateOpen()
{
  COM wordApp;
  COM wordDocuments;

  wordApp = new COM("word.application");
  wordDocuments = wordApp.Documents();

  wordDocuments.Open("c:\\MyBelovedTemplate.dotx");
  wordApp.visible(true);
}

static void WordSaveAs()
{

  COM wordApp;
  COM wordDocuments;
  COM wordDocument;
  COM wordRng;

  wordApp = new COM("word.application");
  wordApp.visible(false);
  wordDocuments = wordApp.Documents();
  wordDocument = wordDocuments.add();
  wordRng = wordDocument.range(0,0);
  wordRng.insertafter("Hallo Microsoft Word");

  wordDocument.SaveAs("C:\\MyBelovedWordDocument.doc");

  wordApp.visible(true);
}

"mason" wrote:

How to send data to Excel

Posted: November 30, 2007 in AX

static void Send_toExcel(Args _args) {

   SysExcelApplication         ExcelApplication;
   SysExcelWorkBooks           ExcelWorkBooks;
   SysExcelWorkBook            ExcelWorkBook;
   SysExcelWorkSheets          ExcelWorkSheets;
   SysExcelWorkSheet           ExcelWorkSheet;
   SysExcelRange               ExcelRange;
   CustTable                   ct;
   int                         row;
   ;
   ExcelApplication = SysExcelApplication::construct();
   ExcelApplication.visible(true);
   ExcelWorkBooks = ExcelApplication.workbooks();
   ExcelWorkBook = ExcelWorkBooks.add();
   ExcelWorkSheets = ExcelWorkBook.worksheets();
   ExcelWorkSheet = ExcelWorkSheets.itemFromNum(1);
   while select * from ct
   {
       row++;
       ExcelWorkSheet.cells().item(row,1).value(ct.AccountNum);
       ExcelWorkSheet.cells().item(row,2).value(ct.Name);
   }

} —Gundhawk 02:30, 15 September 2007 (EDT)

 

=================================================================

static void CreateExcelDokument(Args _args)
{
   SysExcelApplication xlsApplication;
   SysExcelWorkBooks xlsWorkBookCollection;
   SysExcelWorkBook xlsWorkBook;
   SysExcelWorkSheets xlsWorkSheetCollection;
   SysExcelWorkSheet xlsWorkSheet;
   SysExcelRange xlsRange;
   CustTable custTable;
   int row = 1;
   str fileName;
   ;
   // Name des Exceldokuments.
   fileName = "C:\\test.xsl";

   // Excel initalisieren und öffnen.
   xlsApplication = SysExcelApplication::construct();
   xlsApplication.visible(true);

   // Neues Excel Worksheet erzeugen.
   xlsWorkBookCollection = xlsApplication.workbooks();
   xlsWorkBook = xlsWorkBookCollection.add();
   xlsWorkSheetCollection = xlsWorkBook.worksheets();
   xlsWorkSheet = xlsWorkSheetCollection.itemFromNum(1);

   // Zellenüberschriften in das Worksheet schreiben.
   xlsWorkSheet.cells().item(row,1).value(‘Account Num’);
   xlsWorkSheet.cells().item(row,2).value(‘Name’);

   row++;

   // Excel Worksheet mit Daten füllen (Excel-Zellen füllen).
   while select custTable
   {
      xlsWorkSheet.cells().item(row,1).value(custTable.AccountNum);
      xlsWorkSheet.cells().item(row,2).value(custTable.Name);
      row++;
   }

   // Prüfen ob das Dokument schon existiert.
   if(WinApi::fileExists(fileName))
   {
      WinApi::deleteFile(fileName);
   }

   // Excel Dokument speichern.
   xlsWorkbook.saveAs(fileName);

   // Excel schließen.
   xlsApplication.quit();
   xlsApplication.finalize();
}

reset temporary table

Posted: November 14, 2007 in AX
From Kashperuk Ivan
=================
 
You can use 3 ways: 1 is slow, 2 are fast.

Slow way:
delete_from tmpTable;

Fast way 1:
tmpTable = null; // Use this only if you are experienced in using tmpTables
and handling objects

Fast way 2:
void method()
{
    TmpTable tmpTable;

    void clearTmpTable()
    {
        TmpTable tmpTableLocalEmpty;
        ;
        tmpTable.setTmpData(tmpTableLocalEmpty);
    }

    tmpTable.SetTmpData(Class::getData());
    //here tmpTable has data
    clearTmpTable()
    //here tmpTable doesn’t have data
}

Which way to use is for you to decide


Kashperuk Ivan (Vanya), Dynamics AX MCBMSS
My blog – http://kashperuk.blogspot.com
MorphX IT in Russian – http://www.lulu.com/content/723888

"HY Choo" wrote:

> Hi all,
>
> how to reset the temp table to initial state after settmpdata?
>
> TIA
>
> Choo