Lade...
 

Arbeiten mit dem Tree

Arbeiten mit dem Tree

Einleitung

Dieses Topic behandelt den Umgang mit dem Widget ObjectTree.
Es wird Schwerpunktmäßig das Aufbauen eines Tree (SetFormat), Drag-and-Drop mit Update des Objektes (AdjustStructure / AdjustCollection) anhand von Beispielen und Quellcode verdeutlicht.

zum Inhalt

 

Der Aufbau eines Tree

 

Damit ein Tree die Daten auch wie gewünscht (siehe Bild rechts) darstellt, werden kann, müssen spezielle SetFormat- Anweisungen gemacht werden. Hierzu ist eine Kenntnis über die Struktur der Daten, die angezeigt werden sollen, notwendig.

In unserem Beispiel handelt es sich um Aufgaben des Projektplanungsteils von ClassiX® (Trouble-Ticket-System). Es sollen hierbei alle Aufgaben mit ihren Unter- aufgaben abgebildet werden.

Die Struktur der Daten ist im Bild 2 dargestellt. Wenn man sich die Daten so aufzeichnet, dann hat man im Prinzip schon den Baum vor Augen, den das System anzeigen soll. Aber wie erreichen wir das ?

  

Bild1: Der Tree, wie er aussehen soll...
 

Das Code-Snipplet 1 soll diese Frage beantworten. In der Ersten Zeile ist -ganz normal- die Definition des Tree angegeben. Anschliessend werden in dem INITIALIZE die notwendigen SetFormat-Anweisungen gegeben.

Zuerst wollen wir ein Bitmap haben, dass uns die unterschiedlichen Typen anzeigt. Das sind für ein Projekt das Ordner-Symbol, für eine Aufgabe das Käfersymbol und für ein Teil-Projekt ein Ordner-Symbol mit einem Käfer darauf. Hierfür definieren wir uns, wie bei einer normalen ObjectList, eine "Spalte". In diesem Fall wird dort das Makro RfcBitmap aufgerufen.

Als nächstes soll der Status, bestehend aus Nr, Priorität und Termin,  angezeigt werden. Diesen String bauen wir uns im Makro GetAStateForTree zusammen. Der Status soll in der Farbe Grün angezeigt werden.

Die dritte "Spalte" stellt schließlich den Namen des Objektes dar.

Soweit ist das Vorgehen ganz so, wie bei einer ObjectList. Die vierte SetFormat- Anweisung allerdings, macht den Unterschied.

Bild2: die Datenstruktur

Code-Snipplet 1:

ObjectTree(ItemTree, HIDDEN, ACCEPT_DROP, DRAG_COPY, NO_DRAIN, 7, 11, 530, 110)
[  INITIALIZE:
	  [ "CX_REQUEST::call(RfcBitmap)" ] SetFormat
	  [ "CX_REQUEST::call(GetAStateForTree)" COLOR GREEN ] SetFormat
	  [ "CX_REQUEST::shortName" "%-.90s" FIX_WIDTH(150) ] SetFormat
	  [ "CX_REQUEST::subTransactions" NODE ] SetFormat

]

Hier definieren wir, dass für die untergeordneten Knoten (engl. NODE) die Objekte aus der Collection in dem Slot subTransactions verwendet werden sollen. So kann man beliebige Strukturen abbilden.

Aber Vorsicht: Bei Rekursionen gibt es an der entsprechenden Stelle einen Abbruch. Es sei denn, das Flag IGNORE_RECURSION wird verwendet.

zum Inhalt

Update einer Collection nach Drag-and-Drop

In dem Tree, der in Bild 1 dargestellt ist, kann man natürlich auch mittels Drag-and-Drop die Reihenfolge der Objekte verändern. Das Problem dabei ist nur, dass diese Änderungen nicht automatisch in die entsprechende Collections (hier: subTransactions) zurückgeschrieben wird. Das muss extra veranlasst werden.

Dazu können die Befehle AdjustStructure und AdjustCollection verwendet werden. Bei einem Tree ist hier allerdings Vorsicht geboten, denn mit dem Befehl AdjustCollection werden nur die obersten Knoten (bzw. die gerade sichtbaren Objekte) des Tree aktualisiert. Hat man aber ein Objekt mit Unterobjekten, dass sich irgendwo im Tree befindet, so zerstört man sich mit AdjustCollection die Original-Collection!

An dieser Stelle muss dann der Befehl AdjustStructure verwendet werden. Dieser Name ist ein wenig irreführend, denn er bezieht sich nicht auf Struktur-Objekte, (z.B. CX_STRUCTURED) sondern auf die Struktur eines Objektes im Tree. Hierbei muss aber auch mit dem Befehl BackRefName die Rückreferenz angegeben werden. Das Code-Snipplet 2 zeigt den Code, der für unseren Beispiel-Tree verwendet wurde.

Bei der Verwendung von AdjustStructure tritt zusätzlich das Problem der doppeldeutigen NODE SetFormats auf (siehe AdjustStructure).

DROP:  -> objectsDropped
       -> objectSelected
       -> dropMode

       objectsDropped Dup Get(transaction) Dup if Unlink(subTransactions, transaction) else Drop
       objectsDropped objectSelected Dup if Link(subTransactions, transaction) else Drop
       objectSelected ReopenTree
       // do NOT use AdjustCollection here, it will only use the
       // top nodes as the subTransactions for the object
       objectSelected AdjustStructure

Der Drop-Befehl gibt uns drei Parameter auf dem Stack:

  1. einen Vector der gedroppten Objekte
  2. das Objekt, aus das gedroppt wurde
  3. einen "Modus Operandi"

In diesem Fall fehlt ein 0 objectsDropped GetElement -> objectsDropped. Es befand sich in einem längeren Case-Statement, das hier aus Platzersparnis  entfernt wurde (nur für die Anzeige hier in der Doku).

Interessant ist die letzte Zeile mit dem AdjustStructure. Es sollen die subTransactions des gewählten Objektes (objectSelected) und aller Unterobjekte aktualisiert werden. Damit wird auch die Reihenfolge innerhalb der Collection subTransactions aktualisiert. 

Zugriff auf Objekte im Tree

Um auf Objekte im Baum zu verweisen kann man den Befehl Select verwenden. Das Objekt wird dann im Baum blau hinterlegt angezeigt und in den sichtbaren Bereich geholt, falls es vorher außerhalb lag.
Zu beachten ist hierbei, dass nur Objekte selektiert werden können, die dem Tree bekannt sind, dass heißt auf die auch über den Befehl GetCollection zugegriffen werden kann. (Wird eine Position einmal auf- und direkt wieder zugeklappt dann kann auch auf Objekte zugegriffen werden, die nicht sichtbar sind dann aber sichtbar werden.)

Diese Funktionalität kann allerdings nicht in Anspruch genommen werden, wenn man zum Beispiel eine neue Position in einen Baum einfügt und dann direkt markieren möchte. In diesem Fall hat man drei Möglichkeiten:

  1. Wenn man die Position, in die die neue Position eingefügt werden soll, kennt, dann kann man die Position einfach eine Stufe weiter aufklappen. (Anwendungsbeispiel: Man selektiert eine Position im Baum und sagt über das ContextMenu "einfügen")
    baseItem Expand(, this)
    insertedItem Select(, this)
  2. Über einen Algorithmus werden alle übergeordneten Positionen gesammelt und von oben der Reihe nach aufgeklappt.
        Define(ExpandObjectInTree)
          Var(tmpVector) [] -> tmpVector
          do
            Dup ifnot { Drop break }
            Dup tmpVector Insert
            Get(transaction)
          loop
          tmpVector iterate { Expand(, this) }
        ;

    In diesem Beispiel ist allocationTxn das Object, welches im Baum zum Selektieren angezeigt werden soll. Um den Baum also bis hierhin aufzuklappen, müssen alle höheren Positionen aufgeklappt sein. Also wird in der do-loop Schleife rekursiv nach oben gelaufen und die gefundenen höheren Positionen dann in umgekehrter Reihenfolge (zu der Reihenfolge in der Sie aufgesammelt wurden) aufgeklappt.

        // expand all upto the object should be selected
        allocationTXN ExpandObjectInTree
        allocationTXN Select(, this)
     
  3. Es wird einfach der gesamte Baum aufgeklappt und dann die entsprechende Position selektiert.
    -> Dies kann unter Umständen allerdings sehr lange dauern und kann sehr nervig für den Anwender sein.

 

zum Inhalt

InstantView