|  | Unser Angebot für Sie! Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'! |
| |
Zusammenfassung
Lernen Sie ein komplexes Beispiel für das Drag and Drop zwischen verschiedenen Steuerelementen wie dem TreeView- und dem ListView-Steuerelement kennen.
Techniken
Formulare, TreeView, ListView, VBA
Voraussetzungen
Access 2000 und höher
Beispieldateien
aiuTimeplanner.mdb
Shortlink
www.access-im-unternehmen.de/833
Drag and Drop mit TreeView und ListView
André Minhorst, Duisburg
Wenn Sie mit ListView- und TreeView-Steuerelementen arbeiten, sind Drag-and-Drop-Operationen ein tolles Hilfsmittel, um Daten zwischen den Steuerelementen hin- und herzuschieben. In der Anwendung aiuTimeplanner gibt es gleich zwei ListView- und ein TreeView-Steuerelement - genügend Möglichkeiten also, um Drag and Drop einmal anhand eines Praxisbeispiels zu demonstrieren. Dabei sollen Aufgaben aus dem TreeView in beide ListViews verschoben werden sowie von ListView zu ListView - und das Verschieben und Kopieren von Einträgen innerhalb eines ListView-Steuerelements ist ebenfalls gefragt.
Das Hin- und Herschieben von Elementen zwischen verschiedenen ListView- und TreeView-Einträgen ist prinzipiell nicht schwierig. Beim Start des Drag-and-Drop-Vorgangs wird immer das OLEStartDrag-Ereignis ausgelöst, dem Sie mit dem Parameter Data Informationen über die zu verschiebenden Daten mitgeben. Beim Überfahren des Drop-Ziels und beim Fallenlassen werden die beiden Ereignisprozeduren OLEDragOver und OLEDragDrop ausgelöst, denen Sie wiederum über den gleichen Parameter die beim Start übergebenen Daten entnehmen können.
Sie starten also beispielsweise von einem ListView-Steuerelement einen Drag-and-Drop-Vorgang und speisen den Key-Wert des gezogenen Elements in den Parameter Data ein, beispielsweise t123. Dann sollte t einen Hinweis auf das Steuerelement geben, von dem aus der Vorgang gestartet wurde, und 123 entspricht dem Primärschlüsselwert des Datensatzes, der per Drag and Drop bewegt werden soll.
Im Beispiel, das im Beitrag Tagesablauf verwalten mit dem aiuTimeplanner (www.access-im-unternehmen.de/839) genauer erläutert wird, gibt es drei Steuerelemente, die Drag-and-Drop-Operationen unterstützen:
- das ListView-Steuerelement lvwDailyTasks,
- das TreeView-Steuerelement tvwTasks und
- das ListView-Steuerelement lvwTimeplan.
Es gibt gleich eine ganze Reihe möglicher Drag-and-Drop-Operationen, die durch die Pfeile in Abb. 1 verdeutlicht werden:
Abb. 1: Mögliche Drag-and-Drop-Vorgänge in der Beispielanwendung
- Vom TreeView zur Tagesliste: Stellt die Eigenschaft HeuteErledigen auf True ein und fügt den Eintrag zur Tagesliste hinzu.
- Vom TreeView zu einem anderen TreeView-Element: Ordnet die Aufgabe einer anderen Aufgabe unter.
- Vom TreeView an eine leere Stelle im TreeView: Löscht den Eintrag nach Rückfrage. Bei gedrückter Umschalttaste wird ohne Rückfrage gelöscht.
- Vom TreeView zur Tätigkeitsliste: Fügt eine neue Tätigkeit auf Basis der gezogenen Aufgabe hinzu.
- Von der Tagesliste zur Tätigkeitsliste: Fügt eine neue Tätigkeit auf Basis der gezogenen Aufgabe hinzu.
- Von der Tagesliste in einen leeren Bereich der Tagesliste: Stellt die Eigenschaft HeuteErledigen auf False ein und entfernt den Eintrag aus der Liste.
- Von der Tätigkeitsliste an eine andere Stelle der Tätigkeitsliste: Verschiebt die Tätigkeit.
- Von der Tätigkeitsliste an eine andere Stelle der Tätigkeitsliste, diesmal mit gedrückter Strg-Taste: Kopiert die Tätigkeit.
Gezogene Elemente identifizieren
Die Elemente können über den Wert der Eigenschaft Key dem jeweiligen Steuerelement zugeordnet werden, also beispielsweise d für lvwDailyTasks, t für tvwTasks und p für lvwTimeplan. So kann man über den Wert des Parameters Data jeweils am ersten Buchstaben erkennen, von welchem Steuerelement aus der Drag-and-Drop-Vorgang gestartet wurde, die folgenden Zahlen liefern die ID des Elements aus der jeweiligen Tabelle.
Dies wird natürlich etwas schwieriger, wenn Sie etwa in einem TreeView-Steuerelement die Daten aus mehreren Tabellen speichern und beispielsweise Kunden mit dem Präfix k und Artikel mit dem Präfix a versehen.
Dies erhöht jedoch nur die Anzahl der möglichen Kombinationen und damit die verschiedenen Arten, einen Drag-and-Drop-Vorgang anhand von Quelle und Ziel zu verarbeiten. Sie können nach wie vor mit einem Präfix-Buchstaben arbeiten, der die Art der Daten beschreibt, und daran den Primärschlüsselwert des entsprechenden Elements anhängen.
Starten eines Drag-and-Drop-Vorgangs
Der Start eines Drag-and-Drop-Vorgangs beginnt immer mit dem Herunterdrücken der Maustaste auf einem Element eines ListView- oder TreeView-Steuerelements und dem Bewegen des Mauszeigers bei gedrückter Maustaste. Dies ist der Moment, in dem Sie Informationen über das gezogene Element aufnehmen und speichern müssen.
Das passende Ereignis lautet OLEStartDrag und kann über die beiden Kombinationsfelder des Klassenmoduls des Formulars hinzugefügt werden (s. Abb. 2).
Abb. 2: Hinzufügen des OLEStartDrag-Ereignisses
Damit dieses Ereignis und die beiden im Verlauf dieses Beitrags vorgestellten Ereignisse nach Wunsch ausgelöst werden, müssen Sie für die entsprechenden Objektvariablen außerdem noch je zwei Eigenschaften einstellen - dies geschieht in Konfigurationsprozeduren, die im Beitrag Tagesablauf verwalten mit aiuTimeplanner (www.access-im-unternehmen.de/839) beschrieben werden:
objTasks.OLEDragMode = ccOLEDragAutomatic
objTasks.OLEDropMode = ccOLEDropManual
Die drei Ereignisprozeduren objTasks_OLEStartDrag, objDailyTasks_OLEStartDrag und objTimeplan_OLEStartDrag werden dann wie in Listing 1 angelegt und wie die Prozedur objTasks_OLEStartDrag gefüllt.
Listing 1: Diese Prozeduren werden beim Starten eines Drag-and-Drop-Vorgangs ausgelöst.
Private Sub objTasks_OLEStartDrag(Data As MSComctlLib.DataObject, AllowedEffects As Long)
Dim objNode As MSComctlLib.Node
Set objNode = objTasks.SelectedItem
If objNode Is Nothing Then Exit Sub
Data.Clear
Data.SetData objNode.Key, ccCFText
End Sub
Private Sub objTimeplan_OLEStartDrag(Data As MSComctlLib.DataObject, AllowedEffects As Long)
Dim objListitem As MSComctlLib.ListItem
Set objListitem = objDailyTasks.SelectedItem
If objListitem Is Nothing Then Exit Sub
Data.Clear
Data.SetData objListitem.Key, ccCFText
End Sub
Private Sub objDailyTasks_OLEStartDrag(Data As MSComctlLib.DataObject, AllowedEffects As Long)
... wie objTimeplan_OLEStartDrag
End Sub
Die Prozeduren ermitteln jeweils das beim Start des Drag-Vorgangs markierte Element. Ist kein Element markiert, wird die Prozedur beendet. Anderenfalls wird der Parameter Data erst geleert und dann mit dem Wert der Key-Eigenschaft des betroffenen Elements gefüllt, also beispielsweise t123 beim Ziehen eines Elements des Aufgabenbaums, d234 beim Ziehen eines Elements der Tagesliste oder p345 beim Ziehen eines Elements der Tätigkeitenliste.
Damit enthält der Parameter Data genaue Informationen über den Ursprung der Drag-and-Drop-Operation und über den Primärschlüsselwert des gezogenen Elements.
Ziel beim Überfahren markieren
Damit der Benutzer erkennt, wo er ein Element fallenlassen kann und wo nicht, markieren wir potenzielle Ziele beim Überfahren. Dies erledigen die Ereignisprozeduren objTasks_OLEDragOver, objDailyTasks_OLEDragOver und objTimeplan_OLEDragOver. Die Prozeduren sind für alle drei Steuerelemente sehr ähnlich aufgebaut, sodass wir nur eines in Listing 2 komplett abbilden.
Listing 2: Ereignisprozedur beim Überfahren des Zielelements
Private Sub objTasks_OLEDragOver(Data As MSComctlLib.DataObject, Effect As Long, _
Button As Integer, Shift As Integer, x As Single, y As Single, State As Integer)
Dim strData As String
Dim strDatatype As String
If Data.GetFormat(ccCFText) = False Then Exit Sub
strData = Data.GetData(ccCFText)
strDatatype = Left(strData, 1)
Select Case strDatatype
Case "t"
Set objTasks.DropHighlight = objTasks.HitTest(x, y)
End Select
End Sub
Dort wird zunächst das Format des gezogenen Elements geprüft, das den Wert ccCFText aufweisen sollte - anderenfalls wird die Prozedur abgebrochen. Ist das Format korrekt, liest die Prozedur den Inhalt des Parameters Data in eine String-Variable namens strData ein, also beispielsweise t123.
Daraus entnimmt sie den ersten Buchstaben, der einen Hinweis auf die Herkunft des gezogenen Elements liefert. t steht wie erwähnt für objTasks, p für objTimeplan und d für objDailyTasks. Anhand dieses Wertes entscheidet die Prozedur, ob ein Element beim Überfahren als Ziel markiert werden soll.
Dies geschieht, indem mit der HitTest-Funktion des Zielsteuerelements ein Verweis auf das Zielelement ermittelt wird.
Dieser wird anschließend der DropHighlight-Eigenschaft des jeweiligen Steuerelements zugeordnet, was dazu führt, dass das Zielelement durch einen blauen Hintergrund markiert wird.
Für das Objekt objDailyTasks sieht die Prozedur genauso aus wie die für objTasks. Die Prozedur objTimeplan_OLEDragOver enthält jedoch eine andere Bedingung zur Prüfung des Datentyps des gezogenen Elements:
Select Case strDatatype
Case "t", "d", "p"
Set objTimeplan.DropHighlight = objTimeplan.HitTest(x, y)
End Select
Das bedeutet, dass ein Element nicht nur beim Ziehen einer Aufgabe aus dem TreeView-Objekt objTasks als Ziel angeboten wird, sondern auch beim Ziehen von Elementen aus der Tagesliste und aus der Tätigkeitsübersicht selbst.
An dieser Stelle könnten Sie noch verschiedene Symbole einblenden, die einen Hinweis auf die geplante Aktion liefern. Dazu stellen Sie den Parameter Effect auf einen der folgenden Werte ein:
- ccOLEDropEffectCopy
- ccOLEDropEffectMove
- ccOLEDropEffectNone
- ccOLEDropEffectScroll
Element fallenlassen
Kommen wir zu den wirklich interessanten Aktionen - den Datenoperationen und den Änderungen der Darstellung beim Fallenlassen eines Elements auf einem erlaubten Ziel.
Als Erstes sehen wir uns das Fallenlassen eines Elements auf dem TreeView-Objekt des Formulars an. Dies löst die Ereignisprozedur objTasks_OLEDragDrop aus (s. Listing 3). Diese Prozedur enthält ebenfalls einen Parameter namens Data, der den Kennbuchstaben für den entsprechenden Elementtyp und den Primärschlüsselwert liefert.
Listing 3: Beenden des Drag-and-Drop-Vorgangs
Private Sub objTasks_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, _
Button As Integer, Shift As Integer, x As Single, y As Single)
'... Deklaration
strData = Data.GetData(ccCFText)
strDatatype = Left(strData, 1)
lngKeyDrag = Mid(strData, 2)
Set objNodeDrop = objTasks.HitTest(x, y)
Select Case strDatatype
Case "t"
If objNodeDrop Is Nothing Then
AufgabeLoeschen lngKeyDrag, -Shift
Else
strKeyDrop = objNodeDrop.Key
lngKeydrop = Mid(strKeyDrop, 2)
Set objNodeDrag = objTasks.Nodes(strData)
If strKeyDrop = strKeyDrag Then
Exit Sub
End If
Select Case Shift
Case 0 'verschieben
AufgabeVerschieben lngKeydrop, lngKeyDrag
Case 1
AufgabenZusammenfuehren Mid(objNodeDrag.Key, 2), Mid(objNodeDrop.Key, 2)
End Select
FillTree
End If
End Select
End Sub
Dieser Wert wird zunächst ausgelesen. Die einzelnen Bestandteile landen in den beiden Variablen strDatatype und lngKeyDrag. Das TreeView-Steuerelement kann nur Elemente verarbeiten, die auch von dort aus gezogen wurden. Der Wert der Variablen strDatatype muss also t lauten, anderenfalls geschieht nichts.
Danach unterscheidet die Prozedur zwei Fälle, die davon abhängen, ob die Variable objNodeDrop einen Wert enthält oder nicht. Die Variable wird mit einem Verweis auf das TreeView-Element gefüllt, auf dem das Drag-and-Drop-Element fallengelassen wurde. Hat der Benutzer also ein Element auf ein anderes Element gezogen, wird dieses mit objNodeDrop referenziert. Hat er hingegen ein Element an eine freie Stelle innerhalb des TreeView-Steuerelements gezogen, bleibt objNodeDrop leer.
In diesem Fall wird das gezogene Element gelöscht. Es ist nur noch offen, ob es direkt gelöscht werden soll oder ob zuvor noch eine Rückfrage erfolgen soll. Wenn der Benutzer beim Ziehen des Elements in den leeren Raum die Umschalttaste gedrückt hat, wird das Element direkt gelöscht, ohne Betätigen einer Taste erscheint noch eine Rückfrage.
Damit die dadurch aufgerufene Prozedur AufgabeLoeschen je nach Tastenkombination die erforderliche Meldung anzeigt, übergeben Sie dieser den Wert des Parameters Shift mit negativem Vorzeichen (s. Listing 4). 0 bedeutet dann False, -1 heißt True.
Listing 4: Löschen einer Aufgabe
Public Function AufgabeLoeschen(lngAufgabeID As Long, bolLoeschen As Boolean)
Dim db As DAO.Database
Set db = CurrentDb
If bolLoeschen = False Then
bolLoeschen = MsgBox("Aufgabe wirklich löschen?", vbOKCancel + vbExclamation, _
"Aufgabe löschen") = vbOK
End If
If bolLoeschen = True Then
db.Execute "DELETE tblAufgaben.* FROM tblAufgaben INNER JOIN tblAufgabenUnteraufgaben " _
& "ON tblAufgaben.AufgabeID = tblAufgabenUnteraufgaben.UnteraufgabeID "_
& "WHERE tblAufgabenUnteraufgaben.AufgabeID = " & lngAufgabeID, dbFailOnError
db.Execute "DELETE FROM tblAufgaben WHERE AufgabeID = " & lngAufgabeID, dbFailOnError
objTasks.Nodes.Remove "t" & lngAufgabeID
End If
End Function
Der Prozedur übergeben Sie außerdem noch die ID der zu löschenden Aufgabe. Ist bolLoeschen nun False, was geschieht, wenn das Element einfach in den leeren Bereich gezogen wird, fragt die Prozedur nach, ob die Aufgabe gelöscht werden soll. Falls bolLoeschen den Wert True hat, wird direkt gelöscht.
Zurück zur Prozedur objTasks_OLEDragDrop und zu dem Fall, dass das Element auf ein anderes Element gezogen wurde. Dann liest die Prozedur zunächst den Key des Zielelements in die Variable strKeyDrop ein und die ID der entsprechenden Aufgabe in die Variable lngKeyDrop. Um einfacher auf das gezogene Element zugreifen zu können, wird dieses mit der Objektvariablen objKeyDrag referenziert.
Hier erfolgt nun zunächst die Prüfung, ob das Element auf sich selbst gezogen wurde. Dies würde einen Zirkelbezug und damit einen Fehler auslösen, also wird die Prozedur ohne weitere Aktionen beendet. Anderenfalls wird wiederum geprüft, ob der Benutzer die Umschalttaste gedrückt hält (Shift = 1).
Aufgaben zusammenführen
Wozu aber soll dies gut sein? Nun: Das Kopieren von Aufgaben ist wenig sinnvoll, da eine Aufgabe immer nur im Kontext einer übergeordneten Aufgabe stehen und nicht mehrfach vorkommen soll.
Es kann aber vorkommen, dass man feststellt, dass man Aufgaben mehrfach anlegt und pflegt. Um später in Auswertungen vernünftige Daten zu erhalten, müssen diese parallel gepflegten Aufgaben zusammengeführt werden. Dies erledigen Sie, wenn Sie eine Aufgabe bei gedrückter Umschalttaste auf eine andere Aufgabe ziehen:
- Die gezogene Aufgabe wird gelöscht.
- Die Unteraufgaben der gezogenen Aufgabe werden der Zielaufgabe untergeordnet.
- Die Tätigkeiten, die bereits für die gezogene Aufgabe angelegt wurden, werden ebenfalls der Zielaufgabe untergeordnet.
All dies erledigt die Prozedur AufgabenZusammenfuehren mit drei SQL-Statements (siehe Klassenmodul des Formulars frmTagesablauf in der Beispieldatenbank).
Aufgaben verschieben
Bleibt noch das eigentliche Verschieben, das durch bloßes Drag and Drop ohne jegliche Tastenkombination erfolgt. Dies führt zum Aufruf der Prozedur AufgabeVerschieben (s. Listing 5). Diese Prozedur erwartet die Primärschlüsselwerte der beiden beteiligten Aufgaben als Parameter.
Listing 5: Verschieben einer Aufgabe
Private Sub AufgabeVerschieben(lngKeydrop As Long, lngKeyDrag As Long)
Dim db As DAO.Database
Set db = CurrentDb
If lngKeydrop = 0 Then
db.Execute "DELETE FROM tblAufgabenUnteraufgaben WHERE UnteraufgabeID = " _
& lngKeyDrag, dbFailOnError
Else
db.Execute "UPDATE tblAufgabenUnteraufgaben SET AufgabeID = " & lngKeydrop _
& " WHERE UnteraufgabeID = " & lngKeyDrag, dbFailOnError
If db.RecordsAffected = 0 Then
db.Execute "INSERT INTO tblAufgabenUnteraufgaben(AufgabeID, UnteraufgabeID) VALUES(" _
& lngKeydrop & ", " & lngKeyDrag & ")", dbFailOnError
End If
End If
Set db = Nothing
End Sub
Die erste If...Then-Bedingung prüft, ob die Aufgabe auf eine Aufgabe mit dem Primärschlüsselwert 0 gezogen wurde. Die gibt es natürlich gar nicht, aber das Root-Element des Aufgabenbaums hat den Key t0.
Damit ein Element direkt unterhalb dieses Elements angezeigt wird, darf es keine übergeordnete Aufgabe besitzen. Die entsprechende AufgabeID darf also nicht im Feld UnteraufgabeID der Tabelle tblAufgabenUnteraufgaben vorkommen. Um dies zu erreichen, werden schlicht alle Datensätze aus dieser Tabelle gelöscht, die diese Bedingung erfüllen.
Falls die Aufgabe nicht auf das Root-Element, sondern auf eine echte Aufgabe gezogen wurde, versucht die Prozedur zunächst, einen eventuell vorhandenen Eintrag in der Tabelle tblAufgabenUnteraufgaben umzubiegen. Ist keiner vorhanden, war die gezogene Aufgabe wohl zuvor direkt dem Root-Element untergeordnet und es muss ein neuer Eintrag in der Tabelle tblAufgabenUnteraufgaben angelegt werden.
Heutige Aufgaben anlegen
Wenn der Benutzer eine Aufgabe aus dem TreeView in die Liste der heutigen Aufgaben zieht, soll dort ein Eintrag für die entsprechende Aufgabe angelegt werden. Außerdem soll die Eigenschaft HeuteZuErledigen auf den Wert True eingestellt werden.
Beides erledigt die Ereignisprozedur objDailyTasks_OLEDragDrop, die beim Fallenlassen einer Aufgabe im ListView-Steuerelement lvwDailyTasks ausgelöst wird (s. Listing 6). Die Prozedur liest den Wert des Parameters Data ein und ermittelt über den ersten Buchstaben des enthaltenen Ausdrucks die Herkunft des gezogenen Elements. Lautet dieser Buchstabe t, wurde eine Aufgabe aus dem TreeView in die Liste gezogen. In diesem Fall wird die Eigenschaft HeuteZuErledigen dieser Aufgabe auf True eingestellt. Das anschließende Neufüllen des Listenfeldes führt zum Anzeigen des neuen Eintrags.
Listing 6: Heutige Aufgaben anlegen und entfernen
Private Sub objDailyTasks_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, _
Button As Integer, Shift As Integer, x As Single, y As Single)
Dim strData As String
Dim strDatatype As String
Dim lngKeyDrag As Long
Dim db As DAO.Database
Set db = CurrentDb
strData = Data.GetData(ccCFText)
strDatatype = Left(strData, 1)
lngKeyDrag = Mid(strData, 2)
Select Case strDatatype
Case "t"
db.Execute "UPDATE tblAufgaben SET HeuteErledigen = True WHERE AufgabeID = " _
& lngKeyDrag, dbFailOnError
Case "d"
If objDailyTasks.HitTest(x, y) Is Nothing Then
db.Execute "UPDATE tblAufgaben SET HeuteErledigen = False WHERE AufgabeID = " _
& lngKeyDrag, dbFailOnError
End If
End Select
FillDailyTasks
Set db = Nothing
End Sub
Es gibt jedoch noch eine zweite Variante: Der Benutzer kann auch einen vorhandenen Eintrag in den leeren Bereich des ListView-Steuerelements ziehen, um diesen aus der Liste zu entfernen. Das bedeutet, dass der Parameter Data einen mit dem Buchstaben d beginnenden Eintrag liefert. Die Prozedur prüft dann noch, ob der Eintrag tatsächlich auf den leeren Bereich gezogen wurde (objDailyTasks.Hittext(x,y) Is Nothing), und stellt die Eigenschaft HeuteZuErledigen der Aufgabe auf den Wert False ein. Nach der Aktualisierung verschwindet dieser Eintrag aus der Liste.
Tätigkeiten anlegen
Das Listenfeld lvwTimeplan bietet einige Drag-and-Drop-Möglickeiten, die in der Ereignisprozedur objTimeplan_OLEDragDrop abgearbeitet werden (s. Listing 7). Auch diese Prozedur speichert den Wert des Parameters Data, der Informationen über das gezogene Element liefert, in der Variablen strData. Aus dieser wird der erste Buchstabe als Kennzeichen für das Herkunftssteuerelement in der Variablen strDatatype gespeichert. Die ID des Elements landet in der Variablen lngKeyDrag.
Listing 7: Tätigkeiten anlegen und verschieben
Private Sub objTimeplan_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, _
Button As Integer, Shift As Integer, x As Single, y As Single)
'... Variablendeklaration
Set db = CurrentDb
strData = Data.GetData(ccCFText)
strDatatype = Left(strData, 1)
lngKeyDrag = Mid(strData, 2)
Set objDrop = objTimeplan.HitTest(x, y)
If objDrop Is Nothing Then
Exit Sub
End If
strKeyDrop = objDrop.Key
lngKeydrop = Mid(strKeyDrop, 2)
Select Case strDatatype
Case "t" 'from tvwTasks
Set objDrag = objTasks.Nodes(strData)
objDrop.ListSubItems(1).Text = objDrag.Text
Case "d" 'from lvwDailyTasks
Set objDrag = objDailyTasks.ListItems(strData)
objDrop.ListSubItems(1).Text = objDrag.Text
Case "p" 'from lvwTimeplan
Set objDrag = objTimeplan.ListItems(strData)
If strKeyDrop = strKeyDrag Then
Exit Sub
End If
lngKeyDrag = Nz(DLookup("AufgabeID", "tblTaetigkeiten", "TaetigkeitID = " & objDrag.Tag))
If Not IsNull(lngKeyDrag) Then
objDrop.ListSubItems(1).Text = objDrag.ListSubItems(1).Text
If Shift = 0 Then 'verschieben
db.Execute "DELETE FROM tblTaetigkeiten WHERE Taetigkeitdatum = " _
& ISODatum(Me!txtDatum) & " AND Taetigkeitzeit = " _
& ISODatum(objDrag.Text), dbFailOnError
objDrag.ListSubItems(1).Text = ""
End If
End If
End Select
objDrop.Tag = AddTaskToTimeplan(lngKeyDrag, Me!txtDatum, objDrop.Text)
If Not objDrag Is Nothing Then objDrag.Selected = False
Set objTimeplan.DropHighlight = Nothing
Set db = Nothing
End Sub
Danach folgt eine Prüfung, ob ein bestehender Listeneintrag als Ziel der Drag-and-Drop-Operation verwendet wurde. Falls nicht, wird die Prozedur beendet. Falls doch, sammelt die Prozedur die Informatoinen über das Zielelement in den Variablen strKeyDrop und lngKeyDrop. lngKeyDrop entspricht dabei der Position des Zieleintrags.
Zeigt das ListView-Steuerelement 8:00 als ersten Eintrag an und zieht der Benutzer eine Aufgabe auf diesen Eintrag, erhält lngKeyDrop beispielsweise den Wert 1.
Danach folgt eine Select Case-Bedingung, die je nach der Herkunft des gezogenen Elements verschiedene Aktionen durchführt.
Kommt das Element vom Aufgaben-TreeView, wird das Element referenziert und sein Text in die zweite Spalte des Zielelements geschrieben. Gleiches gilt, wenn das Element von der Liste der heutigen Aufgaben auf die Tätigkeitsliste gezogen wurde. Etwas komplizierter wird es, wenn der Benutzer einen Eintrag der Tätigkeitsliste auf einen anderen Eintrag der Tätigkeitsliste gezogen hat. In diesem Fall prüft die Prozedur zunächst, ob der Eintrag auf sich selbst gezogen wurde, und beendet gegebenenfalls die Prozedur.
Anderenfalls liest sie mit einer DLookup-Anweisung die AufgabeID ein, auf welcher die gezogene Tätigkeit basiert. Ist diese nicht Null, stellt die Prozedur den Text des Zielelements auf den Text des Quellelements ein. Nun kommt es noch darauf an, ob das Element bei gedrückter Strg-Taste gezogen wurde. Falls ja, soll das Element kopiert werden, falls nein, wird es verschoben. Ersteren Fall hätten wir bereits erledigt: Das bestehende Element ist noch vorhanden, das neue Element wurde angelegt. Wenn das Element verschoben werden soll, müssen wir das gezogene Element also noch löschen. Dies erledigt die Prozedur mit einer entsprechenden DELETE-SQL-Anweisung. Außerdem wird der Text des entsprechenden Elements noch geleert.
Sicher ist Ihnen aufgefallen, dass die beschriebenen Anweisungen dieser Prozedur bislang zwar eine Menge Elemente verschoben und kopiert haben, jedoch wurde nur das innerhalb der Tätigkeitenliste verschobene Element auch in der zugrunde liegenden Tabelle gelöscht - und immerhin haben wir in der Select Case-Anweisung bereits alle möglichen Fälle abgearbeitet.
Dies ist der Fall, weil die Vorgehensweise zum Anlegen einer neuen Tätigkeit im ListView-Steuerelement lvwTimeplan für alle Konstellationen gleich erfolgt, und zwar über einen Aufruf der Funktion AddTaskToTimeplan (s. Listing 8). Diese erwartet die AufgabeID, das Datum und die Zeit der neuen Tätigkeit.
Listing 8: Tätigkeit zum Timeplan hinzufügen
Private Function AddTaskToTimeplan(lngAufgabeID As Long, datTaetigkeitdatum As Date, _
datTaetigkeitzeit As Date) As Long
Dim db As DAO.Database
Set db = CurrentDb
db.Execute "DELETE FROM tblTaetigkeiten WHERE Taetigkeitdatum = " & ISODatum(datTaetigkeitdatum) _
& " AND Taetigkeitzeit = " & ISODatum(datTaetigkeitzeit), dbFailOnError
db.Execute "INSERT INTO tblTaetigkeiten(Taetigkeitdatum, Taetigkeitzeit, Bezeichnung, " _
& "Beschreibung, AufgabeID, KategorieID, AngelegtAm) SELECT " _
& ISODatum(datTaetigkeitdatum) & ", " & ISODatum(datTaetigkeitzeit) _
& ", Aufgabe, Beschreibung, AufgabeID, KategorieID, " _
& ISODatum(Now) & " AS AngelegtAm FROM tblAufgaben WHERE AufgabeID = " & lngAufgabeID, _
dbFailOnError
AddTaskToTimeplan = db.OpenRecordset("SELECT @@IDENTITY").Fields(0)
End Function
Sie löscht eine eventuell bereits vorhandene Tätigkeit für diese Zeit und legt einen neuen Datensatz in der Tabelle tblTaetigkeiten an, wobei die Daten aus dem Datensatz der Tabelle tblAufgaben entnommen werden, welcher der übergebenen AufgabeID entspricht.
Die Funktion gibt die TaetigkeitID der neuen Tätigkeit als Funktionswert zurück. Dieser wird der Eigenschaft Tag des entsprechenden Eintrags des ListView-Steuerelements lvwTimeplan zugewiesen, damit dieser beispielsweise beim Löschen per Kontextmenü identifiziert werden kann.
Zusammenfassung und Ausblick
Dieses komplexe Beispiel zeigt, wie Sie Drag and Drop für Formulare mit mehreren Drag-and-Drop-fähigen Steuerelementen wie ListView- und TreeView-Steuerelementen implementieren können. Sie können Elemente vom TreeView innerhalb des TreeViews verschieben oder löschen oder diese auf die beiden ListView-Steuerelemente ziehen. Damit definieren Sie heute zu erledigende Aufgaben oder die soeben durchgeführten Tätigkeiten.
Grundlagen zu den hier beschriebenen Techniken finden Sie beispielsweise in den Beiträgen Drag and Drop im TreeView-Steuerelement (http://www.access-im-unternehmen.de/320) oder Drag and Drop mit dem ListView-Steuerelement (http://www.access-im-unternehmen.de/334). |