Archiv der Kategorie: Programmierung

VBA und Access : Datenbankzugriff von VBA auf .mdb-Datei

Aufgabenstellung

Von einer Office/VBA-Anwendung aus, soll auf eine Microsoft Access Datenbank (.mdb-Datei) zugegriffen werden.

Vorraussetzungen

Im VBA Editor im Menüpunkt Verweise wird die Option Microsoft ActiveX ADO Objects angehakt.

Lösung

Selektionsanweisung

Public Sub HoleDaten()
   Dim cn As New ADODB.Connection
   Dim rs As New ADODB.Recordset
   Dim i As Integer

' Datei liegt im aktuellen Projektverzeichnis
   cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & CurrentProject.Path & _
                 "\diedatei.mdb"

   Set rs = cn.Execute("SELECT * FROM einetabelle")

   i = 1
   Do While Not rs.EOF
      Cells(i, 1).Value = rs.Fields("Feld1")
      Cells(i, 2).Value = rs.Fields("Feld2")
      Cells(i, 3).Value = rs.Fields("Feld3")
      Cells(i, 4).Value = rs.Fields("Feld4")
      rs.MoveNext
      i = i + 1
   Loop
   cn.Close
End Sub

Manipulationsanweisung

Public Sub SchubseInMDB()
   Dim cn As New ADODB.Connection
   Dim rs As New ADODB.Recordset

   cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=unfug.mdb"

   Set rs = cn.Execute("INSERT INTO einetabelle(zweck)VALUES ('Unsinn')")
   cn.Close
End Sub

HTML/CSS: Ein kompaktes JQuery-Tutorial

Intention

Persönliche Intention

Meine Intention ist es ein kompaktes JQuery-Tutorial zu veröffentlichen, welches (nach diesem Abschnitt) ein Überblick über die wichtigsten JQuery-Funktionen gibt.

Warum JQuery?

JQuery ist eine Javascript-Klassenbibliothek, welche zahlreiche Funktionen für die Selektion im HTML-Baum (DOM) und dessen Manipulation (HTML-Manipulation wie z.B. das Austauschen von TABLE-Tags oder dem dynamischen Hinzufügen von TR-Tags…) enthält.

Es bietet Funktionen für…

  • Elementselektion im Document Object Model über die Sizzle Selector Engine, die weitgehend den XPATH/CSS-3-Selektoren entspricht
  • Document Object Model-Manipulation (HTML-Dokumente dynamisch ändern)
  • Erweitertes Event-System
  • Hilfsfunktionen
  • Effekte und Animationen (einblenden, ausblenden, aufklappen, zuklappen…)
  • Ajax-Funktionalitäten (POST/GET, JSON…)
  • Zahlreiche frei verfügbare Plug-ins

Wer nutzt JQuery?

Vorwiegend Web-Entwickler, die auf einer etwas niedrigeren Ebene ein Javascript-Framework benötigen. JQuery ist kein High-Level-Framework mit WYSIWYG-Editor.

Moderne AJAX/DHTML-Webanwendungen nutzen immer häufiger High-Level-Frameworks wie GWT, ASP.NET, JSF usw…
Diese Frameworks bieten UI-Komponenten (z.B. Kalender, Tabellen mit DB-Anbindung, Bäume…) an, die herkömmlichen HTML-Seiten durch simples „Draufziehen“ (in einem WYSIWYG-Editor) oder Instanzierung neuer Teile (z.B. in einem UI-Composite-Pattern) die Fähigkeit verleiht sich durch Nutzerinteraktion (z.B. Mausklicks, Tastatureingaben…) zu verändern, ohne die Seite komplett neu zu laden (AJAX/DHTML).
Hierbei findet im Hintergrund eine DOM-Manipulation statt (häufig per AJAX – hier werden z.B. asynchron Abschnitte aus der Datenbank nachgeladen). Der Browser zuckt dabei nicht/Die Seite verschwindet bei einem Klick nicht kurz. Wenn man solche Komponenten selber entwickelt, ist JQuery eine ausgezeichnete Erweiterung zu herkömmlichen Javascript-Funktionen.
Microsoft hat in seiner IDE Visual Studio JQuery integriert und verwendet es bei der Entwicklung von Drag-And-Drop-UI-Komponenten. Aus diesem Grund ist ein Zugriff in Visual Studio auf die Funktionen möglich.

Tutorial

Zu Beginn…

Man benötigt 2 Dinge um mit JQuery zu beginnen

  • Das Einbinden der Bibliothek (herunterladen oder wie hier googlecontent nutzen)
  • Die READY-Funktion welche ein Ereignis ist, das sofort auftritt wenn das HTML-Dokument (oder das DOM) zur Manipulation bereit ist

…JQuery ermöglichen…

Hierfür fügen wir zwischen die HEAD-Tags des HTML-Dokuments die folgenden Skripte hinzu:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js">// <![CDATA[
   
// ]]></script><script type="text/javascript">// <![CDATA[
                                         
	// Hier kommt die READY-Methode (s.u.) hin    
    
// ]]></script>Hello World

… und die Ready-Methode implementieren

Hierfür gibt es 3 Möglichkeiten

1. Vollständig:

jQuery(document).ready(function() {
   // Code hier einfügen
})

2. Mit $ als Alias für die jQuery-Klasse

$(document).ready(function() {
   // Code hier einfügen
})

3. Kurzform:

$(function() {
   // Code hier einfügen
})

Die READY-Methode ist das erste Event, was auftreten kann. Das Ereignis tritt ein, wenn auf das DOM zugegriffen werden kann. Ein ALERT-Fenster in dieser Methode aufzurufen macht nicht viel Sinn, da es nicht notwendig ist auf das geladene DOM zugreifen zu müssen. Dies könnte auch früher passieren.

Einschub: Was ist DOM?

Beim DOM handelt es sich um ein Modell, welches den Zugriff auf Elemente, Teilbäume oder Teil-XML-Strukturen erlaubt. Grundsätzlich sieht das DOM eine HTML-Dokumentes mit einer Tabelle folgendermaßen aus:

Verwendung des Selektions-Teils für den DOM

Bei den Beispielen werde ich nur noch die Kurzform der READY-Methode nutzen.

$(function() {
   // Wähle alle <a>-Tags aus und definiere ein CLICK-Ereignis
   $("a").click(function(event){
       // wird bei jedem klick auf einen Link aufgerufen
       alert('Hello JQuery!');

       // verhindert das Aufrufen des in Link-Zieles
       event.preventDefault();
   });
});
</a>

Wer den obigen Code bereits versteht, hat auch das Selektions-Konzept von JQuery verstanden.

Der erste Aufruf ist immer eine Selektion auf das DOM/HTML-Dokument. Mit $(„a“) (alternativ auch jQuery(„a“) ) wurden alle -Tags im gesamten HTML-Dokument ausgewählt. Das Anhängen von .click hängt dann an die Selektion (also alle Links, bzw. -Tags) ein onClick-Ereignis, welches ein ALERT-Fenster öffnet. Die Methode preventDefault() des Event-Objektes verhindert das Standardverhalten des Links, nämlich den Aufruf der Seite. Somit könnte man für alle Links die Zielseite ändern oder abfangen. Der Funktionskörper function() deklariert eine anonyme, namenlose Methode die ausgeführt wird, wenn das onClick-Ereignis des Links (also des -Tags) aufgerufen wird.

Man kann grundsätzlich alle – oder XML-Tags eines „Baumes“ (nämlich dem DOM) selektieren.

Doch was gibt es für Möglichkeiten der Selektion?

Oberflächlich gesehen gibt es 2 Möglichkeiten…

  1. Kombination aus CSS und XPATH -> $(„div > ul a“)
  2. Nutzung verschiedener Methoden des JQuery-Objektes (z.B. $(„a“).each mit $(this) Referenz

Selektion …

… auf eine TAG-ID
$(„#eineDivID“).addClass(„red“);

Die Raute # sagt der Selektion, das sie ein Attribut id=“..“ suchen soll. Die Selektion beinhaltet dann das Element (z.B. ein DIV). Hier wird diesem DIV die CSS-Klasse .red zugewiesen.

Beispiel:

Div-Inhalt
… auf alle Kind-Elemente   aller Tabellen eines Dokumentes
$(„table > tr > td“).addClass(„blue“);Die Selektion wählt alle td-Tags aller Tabellen aus und fügt diesen die CSS-Klasse .blue hinzu. Für Kind-Elemente (Unter-Tags) wird das Ist-Größer-Als-Zeichen > verwendet.

… auf das letzte
-Element aller Tabelle eines Dokumentes
$(„table > tr > td:last“).hover(function(){$(this).addClass(„green“);},function(){$(this).removeClass(„green“);})

Unter Verwendung vom HOVER-Event, welches 2 Parameter onMouseOver/onMouseOut als „functions“ übergeben bekommt, wird ein Mouse-Over-Event auf alle letzten Zeilen aller Tabellen gelegt. Fährt man mit der Maus drüber nimmt dieses die Farben/Styles an, die in der CSS-Klasse .green definiert wurden. Ist die Maus wieder außerhalb der Zeile wird die Klasse entfernt.

… auf alle 

-Tags, die ein Class-Attribut haben

$(„p[@class]“).hide()

Selektiert alle p-Tags im Dokument, die ein Attribut für eine CSS-Klasse vorgegeben haben class=“…“ und versteckt diese.

… auf den ersten 

-Tag einer Seite

$(„p:eq(0)“)…..
… auf alle sichtbaren DIVs der Seite
$(„div:visible“)…
… auf alle -Tags die unter einem -Tag sind
$(„ul > li“)…. alternativ auch $(„ul/li“)
… auf alle 

-Tags mit einer CSS-Klasse „foo“, die einen Link haben

$(„p.foo[a]“)…
… auf alle -Tags mit einem Link mit dem Text „Register“
$(„li[a:contains(‚Register‘)]“)…
… auf alle -Tags mit dem Name „bar“
$(„input[@name=bar]“)…..
… auf alle angehakten/gecheckten Radio-Buttons
$(„input[@type=radio][@checked]“)….

CSS Selektoren

Selektionen nach Stylesheet folgen der Syntax

E:STYLE

wobei E ein HTML-Element/HTML-Tag bezeichnet.

… auf ein 

-Element als einziges Kind-Element seines Eltern-Tags

$(„p:only-child“) …
… auf ein 

-Tag als letztes Kind-Element seines Eltern-Elements

$(„p:last-child“) …

Mit regulären Vergleichsoperatoren…

… z.B. bei der Suche nach Attributen

… auf alle -Tags, in deren href-Attribut der String /content/gallery vorkommt
$(„a[href*=’/content/gallery‘]“)…
… auf alle 

-Tags dessen Attribut „foo“ mit „bar“ beginnt

$(„p[@foo^=bar]“)
… auf alle 

-Tags dessen Attribut „foo“ mit „bar“ endet

$(„p[@foo$=bar]“)

… will be continued (Fortsetzung folgt…)

Adobe Flex 3: Multi-FilterFunction auf mx:ArrayCollection

Aufgabenstellung

Es wird eine Datenstruktur benötigt, die mehrere FilterFunctions unterstützt und mit der mx:ArrayCollection kompatibel ist.

Problem

mx:ArrayCollection besitzt nur die Eigenschaft für eine einzige Filter-Funktion. Möchte man mehrere Filter setzen, muss man komplexe Funktionen mit Filter-Logik implementieren.

Ansatz

Durch die Erweiterung der mx:ArrayCollection wird eine Methode filterFunctions implementiert, welche mehrere Filter entgegennehmen kann. Die erweiterte BjoernsArrayCollection kann in ArrayCollection gecastet oder ggfs. (z.B. durch eine Schleife) befüllt werden.

Lösung

Folgende Klasse erweritert die normale ArrayCollection um mehrere Filter-Funktionen:

package components
{
  import mx.collections.ArrayCollection;
 
  public class BjoernsArrayCollection extends ArrayCollection
  {
      private var _filterFunctions:Array;
 
      public function BjoernsArrayCollection( source:Array = null )
      {
         super(source);
      }
	
       public function set filterFunctions( filtersArray:Array ):void
       {
         _filterFunctions = filtersArray;
         this.filterFunction = complexFilter;
        }
		
       public function get filterFunctions():Array
       {
         return _filterFunctions;
       }
 
       protected function complexFilter( item:Object ):Boolean
       {
         var filterFlag:Boolean = true;
         var filter:Function;

         for each(filter in filterFunctions)
         {
            filterFlag = filter( item );
            if( !filterFlag ) break;
         }
         return filterFlag;
      }
    }
}

Der Filterfunktionen werden so deklariert:

// Result-Methode von Service liefert eine mx:ArrayCollection, die 
// anhand mehrerer Kriterien gefiltert wird.
public function getResetableOrganizationsHandler(event:ResultEvent):void
{
   // Instanzieren des neuen Objektes
   resetableorgas = new BjoernsArrayCollection();

   // Deklaration der Filter 
   resetableorgas.filterFunctions = 
   [
       filterByProductGroup,
       filterByCompcode,
       filterBySalesdiv,
       filterByMarket
   ];	
		
    // Umsortierung in BjoernsArrayCollection
    resetableorgas.removeAll();
			
    for each(var item:ResetableOrganizationsVO in event.result)
    {
       resetableorgas.addItem(item);				
    }
    
     resetableorgas.refresh();	
}

Der Aufbau der Filter-Funktion:

...
// Rückgabe ist true wenn der Vergleich des Eingegebenen (txtCompcode.text) 
// mit einem Element der ArrayCollection (item.compcode) passt
private function filterByCompcode( item:Object ):Boolean
{
   if( String(item.compcode).indexOf(txtCompcode.text)>-1 )return true;
   return false;
}
...

Ggfs. (z.B. in einem mx:TextInput) die Refresh-Methode aufrufen:

     <mx:TextInput change="{resetableorgas.refresh()}" id="txtCompcode" width="100%"/>

Adobe Flex 3: Rekursive Iteration über alle Controls eines Canvas

Aufgabenstellung

Gelegentlich ist es nötig über alle Komponenten eines mx:Canvas-Elements, einem Container oder einer UIComponent zu iterieren.

Beispiele:
* Deaktivieren von allen Controls eines bestimmten Typs (alle Textfelder ausgrauen)
* Neuschriftung von allen Labels
* Deaktivieren aller Reiter

Ansatz

Durch den Typecast in einen DisplayObjectContainer lässt sich über jedes UI-Element iterieren. Eine Startmethode übergibt das Canvas, bzw. das UI-Urelement. Die Rekursionsmethode castet in den kompatiblen Supertyp DisplayObjectContainer und verfügt über Möglichkeiten der Iteration über seine Eigenschaften.

Lösung

// Startmethode übergibt Urelement der Rekursion
public function startSearch():void
{
searchRecursion(this);
}

// Rekursionsmethode ruft sich selbst auf wenn Child-Elemente v.h.
public function searchRecursion(current:DisplayObjectContainer):void
{
for ( var i:Number=0; i< current.numChildren; i++) { var child:DisplayObject = current.getChildAt(i); trace(getQualifiedClassName(child)); var childContainer:DisplayObjectContainer = child as DisplayObjectContainer; if(childContainer) { searchRecursion(childContainer); } } } [/javascript]

VBA und SAP: Funktionsbausteine aufrufen

Aufgabenstellung

Die Aufgabenstellung sowie der Ansatz ist identisch mit diesem Artikel (Bitte zuerst lesen).

Prämissen / Vorraussetzungen

Die Datei librfc32.dll muss im VBA Editor eingebunden werden (sie ist nach der Installation der SAPGUI verfügbar).

Lösung

Aufbau der Verbindung

Public Function SAP_Logon(username As String, password As String) As Boolean
'*************************************************
'  ANMELDUNG AN SAP
'  ACHTUNG: USER MUSS BERECHTIGUNGEN HABEN!!!!
'*************************************************

    Set FunctionCtrl = CreateObject("SAP.Functions")
    'Objekt für die SAP Verbindung

    Set SapConnection = FunctionCtrl.Connection
   
    SapConnection.Client = "100"
    SapConnection.User = username
    SapConnection.Language = "DE"
    SapConnection.password = password
    
     SapConnection.hostname = "rechnername.firma.com" 'nicht das kuerzel wie DE9
    
    SapConnection.systemnumber = "0"

    If Not SapConnection.Logon(0, True) Then 'True silent - false offen
        MsgBox "Logon failed!!!", vbCritical
        CMS_Logon = False
    Else
        CMS_Logon = True
    End If
End Function

Abmelden

Public Function SAP_Logoff() As Boolean
    SapConnection.LogOff
End Function

Funktionbaustein befüllen und aufrufen

Public Function SAP_Create_Request() As Long
'******************************************************
'  Request erzeugen
'  Strukturen füllen und übergeben, dann Log ausgeben
'******************************************************

On Error GoTo ErrorMSG

    Dim FunctionModule As Object
    Dim e_EXPORTSTRUKTUR As Object
    Dim T_TABELLENSTRUKTUR As Object
    
    Dim te_messtab As Object
    
    Dim lCnt As Long

'   Assign Function Module
    Set FunctionModule = FunctionCtrl.Add("Z_FUBA")
    
'   Set export Variables
    Set e_EXPORTSTRUKTUR = FunctionModule.Exports("P_STRUKTURPARAMETER")

' Set structrue fields in export parameter
    e_EXPORTSTRUKTUR ("ZEXPORTPARAM1") = "BLA"
    e_EXPORTSTRUKTUR ("ZEXPORTPARAM1") = "BLA2"

    Set T_TABELLENSTRUKTUR  = FunctionModule.Tables("T_ZMMMATANF8")
    T_TABELLENSTRUKTUR.appendRow
    T_TABELLENSTRUKTUR(1, "SPALTE1") = 1
    T_TABELLENSTRUKTUR(1, "SPALTE2") = 2

    T_TABELLENSTRUKTUR.appendRow
    T_TABELLENSTRUKTUR(2, "SPALTE1") = 3
    T_TABELLENSTRUKTUR(2, "SPALTE2") = 4

'   Call Function Aufruf
    If FunctionModule.Call = True Then
        Set te_messtab = FunctionModule.Tables("TE_MESSTAB")
        ' Meldungen ausgeben:
        
        Dim intRow As Integer
        For intRow = 1 To te_messtab.RowCount
            If te_messtab(intRow, "ARBGB") = "ZMM0001" And _
                te_messtab(intRow, "MSGNR") = "003" Then

                ' ANForderung wurde erstellt.
                CMS_Create_Request = Val(te_messtab(intRow, "MSGV1"))
            End If
            Debug.Print te_messtab(intRow, "ARBGB")
            Debug.Print te_messtab(intRow, "MSGNR")
            Debug.Print te_messtab(intRow, "NATXT_DE")
            sMSGTXT = te_messtab(intRow, "NATXT_DE")
            Debug.Print te_messtab(intRow, "FLDNAME")
            Debug.Print te_messtab(intRow, "MSGV1")
            Debug.Print te_messtab(intRow, "MSGV2")
            Debug.Print te_messtab(intRow, "MSGV3")
            Debug.Print te_messtab(intRow, "MSGV4")
            Debug.Print "----------------------------------"
        Next
    Else
        CMS_Create_Request = 0
         MsgBox "Error creating the CMS Request." & vbNewLine & _
                "See Log for details", vbCritical
    End If
ErrorMSG:
  
End Function

Die Testfunktion

Public Sub Start()
'*************************************************
'  TESTUMGEBUNG
'*************************************************
    Dim RequestNo As Long
    Dim username As String
    Dim password As String
    
    username = "BJOERN"
    password = "ICHBINDERBESTE"
        
    Call CMS_Logon(username, password)
    RequestNo = SAP_Create_Request   
    Call CMS_Logoff
End Sub

Adobe Flex 3: Charaktere aus Flash Professional CS4 exportieren und einbinden

Aufgabenstellung

Es wird ein Grundgerüst für eine Flex-Anwendung erstellt, auf dessen Basis Animationen und Charaktere, die aus Flash CS4 exportiert worden, gesteuert werden können.

Intention

Da Adobe Flex als Entwicklungsumgebung für Programmierungen wesentlich mehr Features bietet als Flash (Context-Hilfe, Syntax-Highlighting, Design-Time-Prüfungen), wird versucht die Vorteile von Beiden Umgebungen zu nutzen und ein Flashspiel mit Flex, dessen Focus auf der Entwicklung mit Eclipse liegt, zu implementieren.

Ziel

Ziel ist das Auffinden von Bests Practices bei der Spieleentwicklung mit Flash/Flex.

Vorgehensweise 1

Erstellen von Animationen

Es werden 2 Loop-fähige Animationen erstellt, die nachher als Symbol konvertiert werden
Hier:
– Moving Landscape
– Character

Export aus Flash / Import in Flex

  • Flash schließen
  • Adobe Account anlegen
  • Flex Component Kit bei Adobe runterladen
  • Flex Component Kit mit Adobe Extension Installer installieren
  • Nur bei Problem mit Deutschen Versionen: Das in C:\User … … AppData … Adobe Flash CS4 … !!!DE!!! … Configuration-Verzeichnis in das auf der gleichen Ebene befindliche !!!EN!!! Verzeichnis kopieren
  • Flash neu starten
  • Die auf der Stage befindlichen Ebenen markieren
  • Menülieste: Modify -> Convert to Symbol
  • In Library (STRG+L) Symbol anklicken
  • Neues Menü: Command – Convert to Flex Component aufrufen
  • File->Publish aufrufen
  • In der Library auf dem neuen Flex Symbol -> Rechte Maustaste -> Convert to SWC (vorher auch unter Properties die Classnamen angeben)
  • In das Flex Projekt (lib-Verzeichnis) kopieren
  • Die Komponente steht in MXML zur Verfügung, wenn man den unter Properties angegebenen Classnamen aufruft

Versuch die Komponenten über Flex zu steuern

<?xml version="1.0" encoding="utf-8"?>
<mx:Application horizontalScrollPolicy="off" verticalScrollPolicy="off" 
          creationComplete="{onCreationComplete()}" 
         xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*" >
  <mx:Script>
  	<!&#91;CDATA&#91;
  		public function onCreationComplete():void
  		{
  			test3.scaleX=-1;
  		}
  	&#93;&#93;>
  </mx:Script>
 <local:landscape x="1400" y="600" height="600">
 </local:landscape>
 <local:MeinTest id="test3" height="300"  y="370" x="200" >
 </local:MeinTest>	
 <mx:ApplicationControlBar x="0" y="0" width="100%" height="50">
  <mx:Button click="{test3.gotoAndStop(0)}" label="Stop"/>
  <mx:Button click="{test3.gotoAndPlay(0)}" label="Start"/>
  <mx:Button click="{test3.scaleY*=-1;test3.scaleX*=-1}" label="Drehen"/>
 </mx:ApplicationControlBar>
</mx:Application>

Ergebnis

Problem

Die UIMovieClips lassen sich zwar rotieren, aber nicht per scaleX um die eigene Achse drehen.

Abhilfe

Wird noch gesucht

Adobe Flex 3: Composite Pattern für Charakter-Animation mit inverser Kinematik

Aufgabenstellung

Es wird eine Flex-Anwendung entwickelt, die das Grundgerüst für ein 360 Grad Flex-Spiel mit einer Schlange erstellt.

Ansatz

Unter der Verwendung des Composite-Patterns werden die Teile der Schlange zusammengesetzt. Die Teile interagieren über „inverse Kinematik“ miteinander, d.h. jedes Teil ist Autonom, wird aber von seinem Verbindungsteil gezogen.

Die Schlange verfügt über die Eigenschaften:
– gleichmäßig beschleunigte Bewegung (Up-Taste am Keyboard)
– gleichmäßig verzögerte Bewegung (Up-Taste loslassen)
– Rückimpuls (Kollision mit Button)
– Lenkung in der Beschleunigung (Left- und Right-Button)
– Jedes vordere Verbindungsstütz zur Schlange bestimmt die Position des hinteren (inverse Kinematik)

Der Code ist minimal, die Main-Datei besteht lediglich aus der Schlange selbst und der Angabe, welches UI-Objekt eine Barriere (hier „Der böse Button“) sein soll. Diese Barriere verfügt über die Eigenschaften des Back-Impulses bei einer Kollision in der Beschleunigung.

Main-Code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application backgroundColor="#ffffff" xmlns:mx="http://www.adobe.com/2006/mxml" 
                         layout="absolute" xmlns:snake="org.actionscript.snake.*">
  <snake:Snake barrier="{boeserButton}" height="100" width="100" snakelength="100" />
  <mx:Button x="10" y="256" id="boeserButton" width="400" label="Der böse Button" />
</mx:Application>

Lösung

In neuem Browser öffnen (bitte 1x anklicken und dann Pfeiltasten benutzen)

Code

Der Code wurde im Flex-Builder 3 entwickelt und ist als Flex 3-Projekt gezippt.
Adobe Flex 3 Projekt downloaden

SAP und JAVA: Zugriff auf Funktionsbausteine / BAPIs mit dem Java Connector

Aufgabenstellung

Es soll auf Daten aus dem SAP System zugegriffen werden, ohne die Datenbank direkt anzusprechen.

Ansatz

Durch die Verwendung des Java-Connectors (JCo) werden Funktionsbausteine / BAPIs als SAP Schnittstellen verwendet. Die Funktionsbausteine können über die SAP GUI mit dem BAPI-Explorer gesucht und mit der TA 37 getestet werden. Der nachfolgende Artikel beschäftigt sich mit dem Aufruf von einem Java-Server. Der JCo kann im Market Place von SAP kostenlos heruntergeladen werden.

Lösung

Remote Function Call (techn. RPC)

Ein Remote Function Call (RFC) ist die SAP-Implementierung von Remote Procedure Calls (RPC). Über einen RFC lassen sich Funktionsbausteine, bzw. BAPI’s aufrufen.

Ein RFC verwendet die Kommunikationsregeln des CPI-C-Protokolls, welches auf TCP/IP oder LU 6.2 basiert. LU 6.2 war dabei das Kommunikationsprotokoll von R/2-Mainframe-Systemen und wurde bei R/3 durch den einheitlichen TCP/IP-Standard ersetzt.

RFC’s nehmen ausschließlich Call-By-Value-Parameter entgegen, da die Übergabe von Zeigern und Speicheradressen hier keinen Sinn macht. In einem fremden System würde die Speicheradresse auf einen Speicher-Bereich zeigen, in dem keine oder Daten von anderen Anwendungen vorhanden sind.

Funktionsweise des Java Connectors (JCo)

Der Java Connector unterstützt den Aufruf von RFC’s, bzw. BAPIs aus einer externen Java-Umgebung (SAP-inbound-call), sowie den Aufruf externer JAVA-Funktionalitäten aus ABAP-Programmen heraus (SAP-outbound-call). Für die letztgenannte Variante existiert im Example-Verzeichnis des JCo ein Beispiel, in dem ein JCo-Server implementiert wird. Der JCo-Server muss dem SAP-System über die Transaktion SM59 bekanntgemacht werden.

Bei einem SAP-Inbound-Call ist die Vorgehensweise die folgende:

1.) Anmeldung der Java-Applikation am R/3-System

JCO.Client Conection = JCO.createClient(Mandant,
                                        Username,
                                        Password,
                                        Language,
                                        Hostname,
                                        Systemnummer);

Connection.connect();

2.) Herunterladen des Repository für den Funktionsbaustein

IRepository rep = JCO.createRepository("MyRepository", Connection);
IFunctionTemplate template = rep.getFunctionTemplate("BAPI_NAME");
JCO.Function funktion = new JCO.Function(template);

3.) Holen einer Referenz auf die Übergabeparameter

JCO.ParameterList import = funktion.getImportParameterList();
JCO.ParameterList table = funktion.getTableParameterList();

4.) Befüllen des Bausteins

5.) Funktionsbaustein ausführen

Connection.execute(funktion);

6.) Hole Referenz auf Rückgabeparameter

JCO.ParameterList export = funktion.getExportParameterList();
JCO.ParameterList table = funktion.getTableParameterList();

7.) Auslesen des Bausteins

Es werden synchrone (sRFC), asynchrone (aRFC) und transaktionale Aufrufe (tRFC, qRFC), sowie Connection-Pooling unterstützt (auf JAVA- wie auf ABAP-Seite).

SAP lobt zwar die Plattformunabhängigkeit des Connectors, jedoch stößt man bei einfachem Kopieren des Bytecodes auf Probleme, da hier eine plattformabhängige Bibliotheksdatei gekapselt wird, die bei höheren Releases nicht mehr funktioniert. Unter Windows wird die Datei librfc32.dll und unter Linux die Datei librfccm.so gekapselt.

Durch die Kapselung der Datei ist es ohne weiteres möglich, Konnektoren für nicht unterstützte Programmiersprachen zu implementieren.

Ein Tutorial der Zeitschrift „Der Entwickler“, welches Online zur Verfügung steht, zeigt durch Verwendung dieser Bibliothek, wie man Delphi an SAP-Systeme andocken kann. Der Artikel trägt den Namen „Delphi goes SAP“. Da es für Delphi keine Konnektoren wie den Java Connector gibt, ist die Kapselung dieser Datei notwendig.

Parameter von Funktionsbausteinen

Da die meisten SAP-Anwendungen im ABAP-Stack implementiert worden, greift man mit dem JAVA Connector hauptsächlich auf remotefähige Funktionsbausteine zu. Diesen werden entweder ausgefüllte Tabellen, oder Import-Parameter zur Verarbeitung übergeben. Als Ausgabe liefert ein Funktionsbaustein Fehler-Parameter, Export-Parameter, neue Tabellen oder auch die übergebenen Tabellen mit neuen Werten zurück. Hierfür ist es notwendig den syntaktischen Aufbau von JCO-Syntax zu kennen.

Import-Parameter

Import-Parameter kann man sich wie einfache Übergabeparametern aus bekannten Programmiersprachen vorstellen. Ein Import-Parameter entspricht einem Feld, was einen Datentyp beinhaltet (Char, Integer, String, Double…). Es kann keine Array’s, Listen oder Tabellen entgegennehmen.

SAP kennzeichnet hauseigene Funktionsbausteine mit einem vorangehenden I für „Import“. Mit der Transaktion SE16 ist es möglich sich die notwendigen Import-Parameter anzusehen.

Nicht alle Import-Parameter sind erforderlich, es gibt auch optionale Parameter. Nicht jeder Funktionsbaustein hat Import-Parameter.

Der nachstehende Code zeigt, wie mit Hilfe des JAVA Connectors der Funktionsbaustein „CFX_API_USER_GETDETAIL“, der die Detaildaten eines cFolders-Benutzers zurückgibt befüllt wird:

public String gibListeVonUsern()
{
  IRepository repository = JCO.createRepository("MYRepository", client);
  IFunctionTemplate ftemplate = repository
 .getFunctionTemplate("CFX_API_USER_GETDETAIL");
  Function func = ftemplate.getFunction();

  JCO.ParameterList input = func.getImportParameterList();
  input.setValue("KARPBJ01", "I_USER_ID");

  // Funktion aufrufen
  client.execute(func);
  (...)
}

Export-Parameter

Nachdem die Funktion ausgeführt wurde, kann über die Export-Parameter iteriert werden um die Rückgabewerte auszulesen. Dazu hat die Klasse ParameterList die Methode getFieldCount(), welche die Anzahl der Rückgabeparameter zurückliefert. Die Methode getString(index) ermöglich über den inkrementellen Index die Iteration mit einer Schleife. Dadurch lassen sich alle Parameter dynamisch auslesen.

Nachfolgend wird aus der aufgerufenden Funktion über die Export-Parameterliste iteriert und das Ergebnis an den String „informationen“ angehangen.


public String gibListeVonUsern()
{
  (...) //Hier war der Funktionsaufruf

  JCO.ParameterList output = func.getExportParameterList();

  for (int i = 0; i < output.getFieldCount(); i++)
  {
    informationen = informationen + output.getString(i) + "\n";
  }
  return informationen;
}

Changing-Parameter

Im Gegensatz zu Import- und Export-Parametern funktionieren Changing-Parameter in beide Richtungen (bidirektional). Der Aufruf ist dabei Call-By-Reference-ähnlich, da sich nach dem Ausführen des Funktionsbausteins mit dem Befehl client.execute(func); der Wert im Feld ändern kann. Das Befüllen und auslesen funktioniert dabei wie bei den Import- und Export-Parametern.

Table-Parameter

Table-Parameter sind bidirektional. Die Referenz auf eine Tabelle kann geholt werden, bevor die Funktion ausgeführt wurde. Hat man die Referenz auf die Tabelle, so lässt sich diese folgendermaßen befüllen:


  // Iteration über einen Vector
  for (int i = 0; i < vector.size(); i++)
  {
     // Neue Zeile an Tabelle anhängen und auf
     // neue Zeile wechseln
     table.appendRow();

     // schreibe in erste Spalte
     table.setValue((String)vector.get(i),0);

     // schreibe in zweite Spalte
     table.setValue((String)vector.get(i),1);
  }

Nachdem eine Funktion ausgeführt wurde, also nachdem Connection.execute(funktion); ausgeführt wurde, kann eine neue Tabelle zurückgeliefert werden, oder eine bereits befüllte Tabelle neue Werte beinhalten.

Um über eine Rückgabetabelle zu iterieren, gibt es die Funktion setRow(index). Das folgende Beispiel zeigt die Suche nach einem bestimmten String in der 3. Spalte und liefert das Feld „ID“ zurück:


  String rueckgabe = null;
  for (int i=0;i<outputtabelle.getNumRows();i++)
  {
    outputtabelle.setRow(i);

    if (outputtabelle.getString(2).equals(suche) )
    {
      rueckgabe = outputtabelle.getString("ID");
    }
  }

Wie ersichtlich wird, lässt sich die Spalte nicht nur anhand des inkrementellen Indexes ansprechen, sondern auch über den Spaltennamen.

In manchen Fällen ist es nötig eine eigene Tabelle zu erzeugen und diese an einen Funktionsbaustein zu übergeben. Zum Beispiel funktioniert die Zuweisung einer befüllten Ergebnistabelle an eine Übergabetabelle nicht, in diesem Fall muss man die Daten in einer Schleife einfach rauskopieren.

Eine eigene Tabellendefinition erzeugt man in diesem Fall so:


  JCO.MetaData smeta  = new JCO.MetaData("DIS");

  smeta.addInfo("DOKAR",  JCO.TYPE_STRING,  3,  0, 0);
  smeta.addInfo("DOKVR",  JCO.TYPE_STRING ,   2, 0, 0);
  smeta.addInfo("DOKTL",  JCO.TYPE_STRING ,   3, 0, 0);
  smeta.addInfo("DOKNR",  JCO.TYPE_STRING ,   3, 0, 0);

  JCO.Structure structure = new JCO.Structure(smeta); 
  JCO.Table table = new JCO.Table(structure);

Struktur-Parameter

Strukturen gehören zu den Tabellen-Typ-Parametern und sind Sonderfälle. Eine Struktur kann innerhalb einer Tabelle, in Import- oder Export-Parametern auftreten. Hierbei handelt es sich lediglich um eine weitere Aufteilung innerhalb eines einzigen Feldes.

Der folgende Code zeigt, wie eine Referenz auf eine Inputstruktur und auf einen normalen Import-Parameter geholt wird .


JCO.Structure struktur =
func.getImportParameterList().getStructure("SELECT_DOCUMENTDATA");
JCO.ParameterList inputdata = func.getImportParameterList();
struktur.setValue(art, "DOCUMENTTYPE");
inputdata.setValue("X", "GETCLASSIFICATION");
Output.execute(funktion);

Fortsetzung folgt …

Adobe Flex 3: Event-Reihenfolge mit Methode testen

Problem

Adobe Flex 3 definiert eine Reihe von Standard-Events, die über die Kontext-Hilfe bei der Quellcodeingabe mit einem gelbem Blitz gekennzeichnet werden. Häufig nutzt man die Kontext-Hilfe im Flex-Builder (STRG+SPACE ruft sie beim Deklarieren/in der Deklaration eines MXML-Tags auf) um die definierten Events anzuzeigen.

Bei Event-Namen wie creationComplete, applicationComplete, InitializationComplete usw… stellt sich häuft die Frage nach der Reihenfolge

Ansatz

Es werden alle Events, die mit einem gelben Blitz versehen sind, an die deklarierte Methode „eventListener“ übergeben. Diese Methode listet im Debug-Modus über die TRACE-Funktion die Reihenfolge und das Auftreten der Events auf.

Lösung

<?xml version="1.0" encoding="utf-8"?>
<mx:Application added="eventListener(event)"
  addedToStage="eventListener(event)"
  applicationComplete="eventListener(event)"
  creationComplete="eventListener(event)"
  initialize="eventListener(event)"
  preinitialize="eventListener(event)"
  layout="absolute"
  xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:Script>
    <!&#91;CDATA&#91;
      import flash.utils.getQualifiedClassName;
      import mx.events.FlexEvent;
      
      private function eventListener(event:Event):void 
      {
         if (
              (event.target == this) || 
              (event.target == this.myDataGrid) ||
              (event.target == this.myPanel)
          ) 
         {
            trace(getQualifiedClassName(this) + ".eventListener: " +
	       getQualifiedClassName(event) + "." + event.type);
            trace("\ttarget: " + getQualifiedClassName(event.target));
          }
    }
 &#93;&#93;>
  </mx:Script>
  <mx:Panel added="eventListener(event)"
      addedToStage="eventListener(event)"
      creationComplete="eventListener(event)"
      id="myPanel"
      initialize="eventListener(event)"
      preinitialize="eventListener(event)">
  <mx:DataGrid  added="eventListener(event)"
           addedToStage="eventListener(event)"
           creationComplete="eventListener(event)"
           id="myDataGrid"
           initialize="eventListener(event)"
           preinitialize="eventListener(event)"/>
  </mx:Panel>
</mx:Application>

Adobe Flex 3: Drag and Drop API

Aufgabenstellung

UI-Komponenten sollen per Drag and Drop verschoben werden können.

Ansatz

Dieser Artikel beschreibt die notwendigen Flex-APIs (DragManager) für die Implementierung einer Drag and Drop-Funktionalität am Beispiel der (siehe Nachfolgeartikel) eLearning-Anwendung für das V-Modell.

Lösung

1.) Definition der verschiebbaren Komponenten und der Behälter-Komponenten.

<!-- WIRD VERSCHOBEN -->
<mx:Button dragComplete="dragCompleteHandler(event);" 
label="System-Entwurf" id="btnSystementwurf"  />
<mx:Button dragComplete="dragCompleteHandler(event);" 
label="Software-Entwurf" id="btnSoftwareentwurf"  />
..

<!-- WIRD HINEINGESCHOBEN -->
<mx:HBox verticalAlign="middle" verticalScrollPolicy="off" 
horizontalScrollPolicy="off" id="dropField1" />
<mx:HBox verticalAlign="middle" verticalScrollPolicy="off" 
horizontalScrollPolicy="off" id="dropField2" />

2.) Registrierung der Eventlistener


<mx:Application creationComplete="{onCreationComplete()} ...

public function onCreationComplete():void
{
   // Registrierung des Buttons "Anforderungsanalyse" für das Verschieben in ein Feld
   this.btnAnforderungsnanalyse.addEventListener( MouseEvent.MOUSE_DOWN, beginDrag );

   // Registrierung des Buttons "AbnahmeEntwurf" für das Verschieben in ein Feld
   this.btnAbnahmeEntwurf.addEventListener( MouseEvent.MOUSE_DOWN, beginDrag );

...

   // Die DropListener werden an die UI-Komponten, bzw. die Komponeten, in die 
   // die Objekte geschoben werden können, angehängt
   this.dropField1.addEventListener( DragEvent.DRAG_ENTER, acceptDrop );
   this.dropField2.addEventListener( DragEvent.DRAG_ENTER, acceptDrop );
   ..
   this.dropField1.addEventListener( DragEvent.DRAG_DROP, handleDrop );
   this.dropField2.addEventListener( DragEvent.DRAG_DROP, handleDrop );
}
&#91;/actionscript3&#93;

3.) Implementierung der Logik - Hier: Überprüfung ob alles richtig verschoben ist
&#91;actionscript3&#93;
private function dragCompleteHandler(event:DragEvent):void {
     var draggedButton:Button = event.dragInitiator as Button;
     var dragInitCanvas:HBox = event.dragInitiator.parent as HBox;

     var stimmt1:Boolean = getButtons(dropField1).indexOf('btnAnforderungsnanalyse')>-1;
     var stimmt2:Boolean = getButtons(dropField2).indexOf('btnSystemarchitektur')>-1;
               	
     if(stimmt1&&stimmt2) 
     {
          Alert.show('Congratulations you did it!'); 
     }                	 
}  

4.) Welche Buttons sind in den Container? (Hier HBOX)

private function getButtons(box:HBox):String
{
var btnString:String=“;
var numChildren:Number = box.numChildren;

for (var i:int = 0; i < numChildren; i++) { if(box.getChildAt(i)["id"]) { btnString=box.getChildAt(i)["id"]; } } return btnString; } [/actionscript3] 5.) Management der Drag and Drop Funktionalität [actionscript3] public function beginDrag( mouseEvent:MouseEvent ):void { // the drag initiator is the object being dragged (target of the mouse event) var dragInitiator:IUIComponent = mouseEvent.currentTarget as IUIComponent; // the drag source contains data about what's being dragged var dragSource:DragSource = new DragSource(); // add some information to the drag source dragSource.addData( "name", dragInitiator.name ); dragSource.addData( "parent", dragInitiator.parent.name ); // ask the DragManger to begin the drag DragManager.doDrag( dragInitiator, dragSource, mouseEvent, null ); } public function handleDrop( dragEvent:DragEvent ):void { var dragInitiator:Button = Button( dragEvent.dragInitiator ); var dropTarget:HBox = dragEvent.currentTarget as HBox; HBox(dropTarget).addChild(dragInitiator); } public function acceptDrop( dragEvent:DragEvent ):void { var dropTarget:IUIComponent = dragEvent.currentTarget as IUIComponent; var dragSource:DragSource = dragEvent.dragSource; // accept the drop DragManager.acceptDragDrop( dropTarget ); // show feedback DragManager.showFeedback( DragManager.MOVE ); } [/actionscript3]