Monday, April 26, 2010

CLR Fehlermeldungen aufschlüsseln

Die Einbindung von .NET-Code im X++ Sourcecode ist eine hübsche Sache. Was sich mit Native-Code nicht bewerkstelligen lässt, kann unter Umständen mit .NET-Code gelöst werden. Leider sind die Ausnahmen deren Ursprung dem .NET-Framework zuzuschreiben sind meist sehr oberflächlich gehalten.
Folgende statischer Methode automatisiert das Aufschlüsseln der .NET-Ausnahme:

static void cRLExtendException(System.Exception _exception)
{
    System.Exception exception = _exception;
    SysInfoLogStr infoLogStr;
    ;
    if (exception)
    {
        infoLogStr = exception.get_Message();
        if (exception.Equals(exception.GetBaseException()))
        {
            // the most inner exception has reached, now we can write the infolog message and throw the exception
            error(infoLogStr);
            throw Exception::CLRError;
        }
        else
        {
            // the current exception is not the most inner exception, so we just set a infolog prefix
            setprefix(infoLogStr);
            MyClass::cRLExtendException(exception.get_InnerException());
        }

    }
    /* else
    {
        well, there was no CLR exception, so just left out
    }
    */
}

Und so wird die neue Hilfsmethode verwendet:

System.String[] files;
;

try
{
    files = System.IO.Directory::GetFiles('c:\\halliGalli');
}
catch
{
    MyClass::cRLExtendException(CLRInterop::getLastException());
}

So sieht dann die Ausgabe des Infolog-Fensters aus:


In den Codebeispielen wurde die Hilfsmethode auf einer Klasse mit dem Namen 'MyClass' angelegt. Auf welcher Klasse schulssendlich die Methode implementiert wird, sollte aber der Entwickler, beziehungsweise das Entwicklerteam entscheiden.

Monday, April 12, 2010

Communicate with the RunBase Dialog

Sometimes you need to communicate with the RunBase object's dialog. Here are some information what you have to know to make it run well.

The Caller
Say, your RunBase dialog calls a custom form which contains special selection settings for the RunBase object (which f.ex. cannot be integrated in the RunBase dialog due layout specifications). So you've placed a menu item button to your dialog. That way the user can call the custom form with the special selection settings:

DialogRunbase ret;
;
ret = super(dialog, forceOnClient);
ret.addMenuItemButton(MenuItemType::Display, 
    menuItemDisplayStr(MyRunBaseSelectionForm));

But once the custom form is open, how to geht a handle to the RunBase object? To grab the RunBase object you need to dig the callers:

FormRun         formRunCaller;
DialogRunBase   dialogRunBase;
MyRunBaseClass  caller;
;
formRunCaller = element.args().caller(); // returns the dialog's form

// returns a DialogRunBase object
dialogRunBase = formRunCaller.args().caller();


// finally returns the RunBase object
caller = dialogRunBase.runBaseBatch(); 

Once you have the handle to the RunBase object you can acceed all public parameter methods and even the RunBase's QueryRun object.

Notify the caller
If the user decide to close your custom form with confirmation, so you need to notify the RunBase object about the new data. The best way is to place this code in the method closeOK() on your custom form:

public void closeOk()
{
    QueryRun qr = caller.queryRun();
    ;
    qr.whatEverYouHaveToManipulate(...);
    caller.parmThisAndThat(...);
    super();
}

Update the dialog values
As the user shall see the setting changes, you need to update the dialog controls. That for you can use the method dialogUpdate():

public void closeOk()
{
    QueryRun qr = caller.queryRun();
    ;
    qr.whatEverYouHaveToManipulate(...);
    caller.parmThisAndThat(...);
    caller.dialogUpdate(); // updates at least query range fields
    super();
}

If you want a little tutorial, you can download this sample project, demonstrates just what I tried to explain. Import the XPO file and open the class MyRunBaseClass, then click on the button "Pick customer".
Keep in mind, it's only a tutorial, the code is neither BP nor fool proofed!

Hope this can improve your coding skills :)

Mit dem Dialog eines RunBase-Objektes kommunizieren

Manchmal ist es nötig mit dem Dialog eines RunBase-Objekts zu kommunizieren. Nachstehend ein paar nützliche Informationen damit es auch gelingt.

An den Aufrufer gelangen
Angenommen der RunBase-Dialog ruft ein weiteres Formular auf, welches über spezielle Selektionseinstellungen für das RunBase-Objekt verfügt (z.B. wenn die Darstellung dieser speziellen Einstellungen im RunBase-Dialog nicht möglich ist). So wird dem Dialog also ein Menu-Item-Button hinzugefügt, damit der Benutzer das Formular mit den speziellen Einstellungsmöglichkeiten aufrufen kann:

DialogRunbase ret;
    ;
    ret = super(dialog, forceOnClient);
    ret.addMenuItemButton(MenuItemType::Display, menuItemDisplayStr(MyRunBaseSelectionForm));

Wie erhält nun aber das Formular Zugriff auf das RunBase-Objekt? Dafür muss durch die Aufrufer 'gegraben' werden:

FormRun         formRunCaller;
    DialogRunBase   dialogRunBase;
    MyRunBaseClass  caller;
    ;
    formRunCaller = element.args().caller(); // returns the dialog's form
    dialogRunBase = formRunCaller.args().caller(); // returns a DialogRunBase object
    caller = dialogRunBase.runBaseBatch(); // finally returns the RunBase object

Wenn man über das Handle des RunBase-Objekts verfügt, hat man Zugriff auf alle öffentlichen Parametermethoden und auch auf das QueryRun-Objekt des RunBase-Objekts.

Den Aufrufer benachrichtigen
Wenn der Benutzer nun das Formular mit den speziellen Selektionskriterien mit OK schliesst, muss das RunBase-Objekt mit den allenfalls vorgenommenen Einstellungen aktualisiert werden. Am besten wird dies in der closeOK()-Methode auf dem Formular implementiert:

public void closeOk()
{
    QueryRun qr = caller.queryRun();
    ;
    qr.whatEverYouHaveToManipulate(...);
    caller.parmThisAndThat(...);
    super();
}

Dialogfelder aktualisieren
Damit der Benutzer die gemachten Änderungen auf dem RunBase-Dialog sieht, müss zusätzlich die Dialogfelder aktualisiert werden, dies wird mit der Methode dialogUpdate() sichergestellt:

public void closeOk()
{
    QueryRun qr = caller.queryRun();
    ;
    qr.whatEverYouHaveToManipulate(...);
    caller.parmThisAndThat(...);
    caller.dialogUpdate(); // updates at least query range fields
    super();
}

Ein kleines sample steht als XPO-Datei zum Download bereit und demonstriert, was ich hier zu erklären versucht war. Nach dem Importieren der XPO-Datei einfach die Klasse MyRunBaseClass öffnen und auf die "Pick customer"-Schaltfläche drücken.
Dieser Beispielcode wurde weder auf Optimale Verfahren geprüft, noch ist er gegen Fehler gefeit!

Hoffe das dieser Beitrag den Entwicklerhorizont ein klein wenig erweitert :)