Zusammenfassung
Erfahren Sie, wie Sie Elemente im Treeview mit Drag and Drop verschieben.
Techniken
Formulare, Treeview-Steuerelement, VBA
Voraussetzungen
Access 2002 und höher oder Access 97/2000 und Developer Edition oder Visual Studio 6.0
Beispieldateien
Treeview2002.mdb
Drag and Drop im Treeview-Steuerelement
André Minhorst, Duisburg
Das Treeview-Steuerelement lässt sich sehr gut zur Darstellung von hierarchischen Daten verwenden. Im Gegensatz zu den üblichen Access-Steuerelementen unterstützt es auch Drag and Drop. Wie das im Detail funktioniert, erfahren Sie im vorliegenden Beitrag.
Drag and Drop aktivieren
Drag and Drop-Operationen erfordern je nach Anwendungsfall einige Zeilen Code. Voraussetzung ist, dass Sie die Einstellung für die Eigenschaften OLEDragMode auf ccOLEDragAutomatic und OLEDropMode auf ccOLEDropManual festlegen. Das können Sie im Eigenschaftsdialog des Treeview-Steuerelements erledigen (s. Abb. 1) oder per Code:
objTreeview.OLEDragMode = _ ccOLEDragAutomatic
objTreeview.OLEDropMode = ccOLEDropManual
Kein Drag and Drop ohne VBA
Außerdem sind einige Ereignisprozeduren mit Leben zu füllen. Diese lassen sich nicht wie bei den üblichen Access-Steuerelementen über das Eigenschaftsfenster anlegen, sondern müssen direkt im VBA-Editor erstellt werden.

Abb. 1: Eigenschaften eines Treeview-Steuerelements
Dazu deklarieren Sie zunächst eine Objektvariable für das Treeview-Steuerelement, das Sie in der Beim Laden-Ereignisprozedur des Formulars zuweisen (s. Quellcode 1).
Quellcode 1: Deklarieren und Zuweisen des Treeview-Objekts
Dim objTreeview As MSComctlLib.Treeview
Private Sub Form_Load()
Set objTreeview = Me.ctlTreeview.Object
End Sub
Allein durch die Deklarationszeile können Sie innerhalb der Prozeduren per IntelliSense auf die einzelnen Eigenschaften und Methoden des Treeview-Steuerelements zugreifen und dessen Ereignisprozeduren mit den beiden Kombinationsfeldern im Kopf des VBA-Codefensters anlegen.

Abb. 2: Anlegen von Ereignisprozeduren im VBA-Codefenster
Auf diese Weise sind die nachfolgend benötigten drei Prozeduren schnell erstellt (s. Abb. 2). Das Ereignis OLEStartDrag wird ausgelöst, wenn Sie den Mauszeiger auf einem Eintrag positionieren, die linke Maustaste gedrückt halten und den Mauszeiger dabei bewegen.
Das Ereignis OLEDragOver wird nach dem Ereignis OLEStartDrag in kurzen Abständen ausgelöst - es könnte eigentlich OLEDragMouseMove heißen -, bis Sie die Maustaste loslassen. Dies wiederum ruft das Ereignis OLEDragDrop auf den Plan.
Damit hätten Sie das Werkzeug beisammen - mit Ausnahme einiger nützlicher Eigenschaften des Treeview-Steuerelements:
HitTest(x,y): Liefert einen Verweis auf den Knoten mit den Koordinaten x und y.
SelectedItem: Legt den markierten Knoten fest oder liest diesen aus.
DropHighlight: Markiert den Knoten, über dem sich der Mauszeiger bei einer Drag and Drop-Operation befindet, sofern dies nicht der Ausgangsknoten ist.
Sobald Access den Start einer Drag and Drop-Operation erkennt, soll kein Knoten mehr markiert sein. Das erledigt der Einzeiler aus Quellcode 2.
Aber warum soll die Markierung des aktuell ausgewählten Knotens aufgehoben werden? Ganz einfach: Dieser Knoten ist nicht unbedingt mit dem identisch, der per Drag and Drop verschoben werden soll.
Dementsprechend wird in der während des Drag and Drop-Vorgangs immer wieder ausgeführten Ereignisprozedur OLEDragOver zunächst der zu verschiebende Eintrag über die Eigenschaft SelectedItem als aktueller Eintrag markiert.
Dies passiert nur einmal direkt nach dem Starten des Drag and Drop-Vorgangs, nachdem die OLEStartDrag-Prozedur den vorherigen aktiven Knoten abgewählt hat.
Bei jedem Aufruf der OLEDragOver-Prozedur passiert allerdings Folgendes: Mit der Eigenschaft DropHighlight wird der Knoten, über dem sich der Mauszeiger derzeit befindet, markiert, um ihn als Ziel der Operation zu kennzeichnen.
Das große Finish folgt dann in der Ereignisprozedur OLEDragDrop. Diese wird beim Loslassen der Maustaste ausgelöst und muss dafür sorgen, dass der Knoten an die neue Stelle verschoben wird.
Aber nicht nur das: Das Abbild der Daten im Treeview-Steuerelement und die in den Tabellen befindlichen Daten werden natürlich nicht automatisch synchronisiert.
Also müssen Sie auch noch dafür sorgen, dass der entsprechende Datensatz an die neue Situation im Treeview-Steuerelement angepasst wird.
Das hört sich zwar erst einmal recht kompliziert an, ist aber letzten Endes reine Fleißarbeit. Im vorliegenden Beispiel sieht die Routine wie in Quellcode 4 aus. Es gibt beim Drag and Drop grundsätzlich zwei zu unterscheidende Fälle:
Der Knoten wird in den leeren Raum gezogen und soll so in die oberste Hierarchie-Ebene eingeordnet werden.
Der Knoten wird auf einen anderen Knoten gezogen und soll so diesem untergeordnet werden.
Quellcode 2: Aufheben der aktuellen Markierung
Private Sub ctlTreeview_OLEStartDrag(Data As Object, _ AllowedEffects As Long)
objTreeview.SelectedItem = Nothing
End Sub
Quellcode 3: Diese Routine wird immer wieder während des Drag and Drop-Vorgangs ausgeführt.
Private Sub ctlTreeview_OLEDragOver(Data As _ Object, Effect As Long, Button As _ Integer, Shift As Integer, x As Single, _ y As Single, State As Integer)
If objTreeview.SelectedItem Is Nothing Then
Set objTreeview.SelectedItem = objTreeview.HitTest(x, y)
End If
Set objTreeview.DropHighlight = objTreeview.HitTest(x, y)
End Sub
Nach dem Ermitteln einiger Informationen des Knotens, der verschoben werden soll (Primärschlüsselwert des entsprechenden Datensatzes, Key, angezeigter Text), zweigt eine If Then-Bedingung die Routine entsprechend den beiden oben genannten Fällen auf.
Beim Ziehen in den leeren Raum wird zunächst der Knoten entfernt und neu angelegt. Leider nicht ohne Reibungsverluste, denn eventuell untergeordnete Knoten gehen so verloren. Um diese wiederherzustellen, wird die Änderung zunächst in der Tabelle Personal durchgeführt:
Der Wert des Feldes Vorgesetzte(r) des verschobenen Datensatzes wird auf NULL gesetzt. Anschließend ruft die Routine die Prozedur TreeviewRekursivFuellen mit dem neuen Knoten als Parameter auf, um die untergeordneten Knoten wiederherzustellen.
Diese Routine wird auch zum Aufbauen eines Baumes auf Basis von Daten aus Tabellen mit reflexiver Beziehung verwendet - mehr dazu im Beitrag Treeview-Steuerelement mit Daten füllen (s. Shortlink 321).
Zieht der Benutzer den Knoten auf einen anderen Knoten, ist etwas weniger zu tun:
Nach einer Prüfung, ob der Knoten nicht auf sich selbst abgelegt werden soll, ermittelt die Routine zunächst den Primärschlüsselwert des neuen übergeordneten Knotens.
Anschließend weist man der Parent-Eigenschaft des verschobenen Knotens den neuen übergeordneten Knoten zu. Damit werden auch automatisch alle untergeordneten Knoten mitgezogen.
Fehlt noch die Übernahme der Änderung in die Datenbank mit der entsprechenden UPDATE-Abfrage.
Das Formular inklusive Drag and Drop-Funktionalität sollte nun wie in Abb. 3 aussehen und das Verschieben der Einträge im Treeview-Steuerelement ermöglichen. Für jede Verschiebung tragen die entsprechenden Routinen die Änderungen in die dem Treeview-Steuerelement zugrunde liegende Tabelle ein.

Abb. 3: Drag and Drop in Aktion
Quellcode 4: Durchführen der Drag and Drop-Operation
Private Sub ctlTreeview_OLEDragDrop(Data As Object, Effect As Long, Button As Integer, _ Shift As Integer, x As Single, y As Single)
Dim db As DAO.Database
Dim lngID As Long
Dim lngParentID As Long
Dim strKey As String
Dim strText As String
Set db = CurrentDb
'zu verschiebenden Datensatz ermitteln
lngID = Mid(objTreeview.SelectedItem.Key, 9)
strKey = objTreeview.SelectedItem.Key
strText = objTreeview.SelectedItem.Text
'Wurde an eine freie Stelle oder auf einen anderen Knoten gezogen?
If objTreeview.HitTest(x, y) Is Nothing Then
'Knoten entfernen
objTreeview.Nodes.Remove objTreeview.SelectedItem.Index
'Knoten neu anlegen
objTreeview.Nodes.Add , , strKey, strText, 1
'Änderung in die Datenbank übertragen
db.Execute "UPDATE Personal SET [Vorgesetzte(r)] = NULL WHERE [Personal-Nr] = " & lngID
'Untergeordnete Elemente des verschobenen Knotens wiederherstellen
TreeviewRekursivFuellen lngID
Else
If objTreeview.SelectedItem <> objTreeview.DropHighlight Then
'neuen übergeordneten Datensatz ermitteln
lngParentID = Mid(objTreeview.DropHighlight.Key, 9)
'Zielknoten als neuen übergeordneten Knoten festlegen
Set objTreeview.SelectedItem.Parent = objTreeview.DropHighlight
'Änderung in die Datenbank übertragen
db.Execute "UPDATE Personal SET [Vorgesetzte(r)] = " _ & lngParentID & " WHERE [Personal-Nr] = " & lngID
End If
Set objTreeview.DropHighlight = Nothing
End If
End Sub
Zusammenfassung und Ausblick
Mit der Drag and Drop-Operation in Zusammenhang mit dem Treeview-Steuerelement lassen sich hierarchische Daten ganz einfach mit der Maus zuordnen.
Das vorliegende Beispiel bezieht sich auf ein Treeview-Steuerelement, das mit Daten aus einer Tabelle mit reflexiver Beziehung gefüllt wurde.
Die Vorgehensweise bei Treeviews mit Daten aus mehreren verknüpften Tabellen unterscheidet sich zwar von dem hier vorgestellten Beispiel, kann aber mit dem enthaltenen Know-how nachgebildet werden.
Natürlich fehlen dem Beispiel noch Features: So sollten Knoten, die in den freien Raum gezogen werden, eigentlich direkt in alphabetischer Reihenfolge eingeordnet anstatt einfach unten angehängt werden.
|