Zur Hauptseite ... Zum Onlinearchiv ... Zum Abonnement ... Zum Newsletter ... Zu den Tools ... Zum Impressum ... Zum Login ...

Achtung: Dies ist nicht der vollständige Artikel, sondern nur ein paar Seiten davon. Wenn Sie hier nicht erfahren, was Sie wissen möchten, finden Sie am Ende Informationen darüber, wie Sie den ganzen Artikel lesen können.

Kompletten Artikel lesen?

Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

E-Mail:

Gedrucktes Heft

Diesen Beitrag finden Sie in Ausgabe 2/2006.

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'!

Diesen Beitrag twittern

Zusammenfassung

Erfahren Sie, wie Sie mit Drag and Drop im ListView m:n-Beziehungen verwalten und individuelle Reihenfolgen anpassen können.

Techniken

ListView-Steuerelement, VBA

Voraussetzungen

Access 2000 und höher

Beispieldateien

ListView.mdb, MSCOMCTL.msi

Drag and Drop mit dem
ListView-Steuerelement

André Minhorst, Duisburg

Listenfelder sind eine schöne Einrichtung. Wie schade, dass sie kein Drag and Drop erlauben. Aber vielleicht haben Sie Glück und setzen Access 2002 oder höher ein oder besitzen ein passendes Entwicklerpaket: Dann können Sie nämlich das ListView-Steuerelement verwenden und mit diesem die gewünschte Drag and Drop-Funktion realisieren.

Beispielanwendung

Beispiele für zwei Listenfelder, zwischen denen Daten hin und her verschoben werden sollen, gibt es wie Sand am Meer. Oft handelt es sich dabei um die Abbildung von m:n-Beziehungen. Warum also nicht mal wieder das gute alte Verteiler-Beispiel aufgreifen? Hier geht es darum, für eine Publikation eine Reihe Empfänger aus den vorhandenen Kontakten auszuwählen.

Das Beispiel basiert auf drei Tabellen: Die Tabelle tblPublikationen enthält die zu veröffentlichenden Werke, die Tabelle tblEmpfaenger die Adressen und die Tabelle tblVerteiler verknüpft die Einträge der einen mit denen der anderen Tabelle. Im Beziehungsfenster sieht das wie in Abb. 1 aus.

Abb. 1: Datenmodell der Beispieldatenbank

Das Formular soll in zwei ListView-Steuerelementen die Empfänger und die Nicht-Empfänger der Publikation anzeigen (s. Abb. 2).

Abb. 2: So soll das Formular zum Hin- und Herschieben von Empfängern aussehen.

Quellcode 1: Diese Routine füllt eine Liste mit den Daten einer Tabelle

Private Sub ListeAktualisieren(objListView As ListView, strSQL As String)

    Dim db As DAO.Database

    Dim rst As DAO.Recordset

    Set db = CurrentDb

    Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)

    objListView.ListItems.Clear

    Do While Not rst.EOF

        objListView.ListItems.Add , "a" & rst(0), rst(1)

        rst.MoveNext

    Loop

    rst.Close

    Set rst = Nothing

    Set db = Nothing

End Sub

Quellcode 2: Aufruf der Routine zum Füllen der beiden ListView-Steuerelemente

Private Sub Form_Current()

    Dim strSQLEmpfaenger As String

    Dim strSQLKeinEmpfaenger As String

    If Not IsNull(Me.Publikation) Then

        strSQLEmpfaenger = "SELECT tblEmpfaenger.EmpfaengerID, tblEmpfaenger.Empfaenger " _
            & "FROM tblEmpfaenger INNER JOIN tblVerteiler ON tblEmpfaenger.EmpfaengerID = " _
            & " tblVerteiler.EmpfaengerID WHERE tblVerteiler.PublikationID = " _
            & Me.PublikationID & " ORDER BY tblEmpfaenger.Empfaenger"

        strSQLKeinEmpfaenger = "SELECT tblEmpfaenger.EmpfaengerID, tblEmpfaenger.Empfaenger " _
            & "FROM tblEmpfaenger WHERE tblEmpfaenger.EmpfaengerID NOT IN " _
            & "(SELECT tblEmpfaenger.EmpfaengerID FROM tblEmpfaenger INNER JOIN tblVerteiler " _
            & "ON tblEmpfaenger.EmpfaengerID = tblVerteiler.EmpfaengerID WHERE " _
            & " tblVerteiler.PublikationID = " & Me.PublikationID _
            & ") ORDER BY tblEmpfaenger.Empfaenger"

        ListeAktualisieren Me.lvwZugeordnet.Object, strSQLEmpfaenger

        ListeAktualisieren Me.lvwNichtZugeordnet.Object, strSQLKeinEmpfaenger

    End If

End Sub

Füllen der Listenfelder

Bevor überhaupt irgendein Eintrag von links nach rechts und umgekehrt gezogen werden kann, müssen erst einmal überhaupt Einträge da sein - dementsprechend folgt nun die Beschreibung des Aufbaus des Formulars.

Das Formular selbst ist an die Tabelle tblPublikationen gebunden. Bei jedem Wechsel des im Formular angezeigten Publikations-Datensatzes sollen auch die beiden ListViews aktualisiert werden. Die Routine wird daher logischerweise durch die Ereigniseigenschaft Beim Anzeigen aufgerufen.

Die beiden ListViews sind fast identisch aufgebaut, was sich auch auf das Füllen mit Daten auswirkt: Die dazu benötigte Routine sieht für beide Steuerelemente fast identisch aus. So identisch, dass man daraus eine einzige parametrisierte Routine machen kann. Als Parameter übergibt die aufrufende Prozedur nur einen Verweis auf das jeweilige Listenfeld sowie den SQL-Ausdruck, der die einzufügenden Datensätze festlegt.

Die Routine ListeAktualisieren ist für das Anlegen der Elemente der ListViews verantwortlich (s. Quellcode 1). Sie öffnet eine auf der übergebenen SQL-Anweisung basierende Datensatzgruppe und durchläuft diese. Dabei fügt sie je Datensatz ein Element zum angegebenen ListView hinzu. Das erste im SQL-Ausdruck angegebene Feld dient als Teil der Key-Eigenschaft des Elements, das zweite als angezeigter Text. Die beim Anzeigen des aktuellen Datensatzes im Formular ausgelöste Prozedur Form_Current ruft diese Routine zweimal mit verschiedenen Parametern auf (s. Quellcode 2).

Der erste dabei verwendete SQL-Ausdruck ermittelt alle Datensätze der verknüpften Tabellen tblEmpfaenger und tblVerteiler, bei denen die in der Tabelle tblVerteiler angegebene PublikationID mit der aktuell im Formular angezeigten übereinstimmt.

Quellcode 3: Der Drag-and-Drop-Vorgang startet im ListView lvwNichtZugeordnet ...

Private Sub lvwNichtZugeordnet_OLEStartDrag(Data As Object, AllowedEffects As Long)

    Dim objListItem As ListItem

    Dim strData As String

    Dim strDataItems() As String

    For Each objListItem In lvwNichtZugeordnet.ListItems

        If objListItem.Selected = True Then

            strData = strData & objListItem.Key & "¦" & objListItem.Text & ";"

        End If

    Next

    Data.Clear

    Data.SetData strData, ccCFText

End Sub

Die zweite SQL-Anweisung verwendet fast die gleiche Anweisung wie die erste - allerdings nur als Unterabfrage. Damit ermittelt diese Abfrage alle Empfänger-Datensätze, die nicht durch die erste SQL-Anweisung erfasst werden und dementsprechend nicht über die Tabelle tblVerteiler mit der aktuell angezeigten Publikation verknüpft sind.

Aufbau der ListView-Steuerelemente

Die beiden ListViews sind genau gleich aufgebaut. Die einzigen Unterschiede sind der Name und die angezeigte Spaltenüberschrift. Die Eigenschaften im Detail:

  • Name: lvwEmpfaenger beziehungsweise lvwKeinEmpfaenger
  • View: 3 - lvwReport
  • Spaltenköpfe: je einer mit einer Breite von 2500 und der Beschriftung Empfänger beziehungsweise Kein Empfänger
  • MultiSelect: aktiviert
  • OLEDragMode: 1 - ccOLEDragAutomatic
  • OLEDropMode: 1 - ccOLEDropManual
  • Mit diesen Einstellungen und den beiden beschriebenen Routinen können Sie nun bereits die Daten aus den drei Tabellen darstellen. Es fehlt also nur noch die Drag-and-Drop-Funktion ...

    Drag and Drop

    Beim Drag and Drop mit ungebundenen Steuerelementen, wie es die ListViews nun einmal sind, ist vor allem eines zu beachten: Neben dem Verschieben des Eintrags zwischen den Listen müssen auch die dahinter liegenden Datenbanktabellen aktualisiert werden. Für das Ziehen eines Eintrags von einem zum anderen ListView benötigen Sie nur zwei Ereignisprozeduren - vorausgesetzt, Sie verzichten auf jeglichen Schnickschnack:

  • OLEStartDrag beim Quell-ListView
  • OLEDragDrop beim Ziel-ListView
  • Die OLEStartDrag-Prozedur (s. Quellcode 3) durchläuft in einer Schleife alle Elemente des ListViews und prüft, welche Elemente markiert sind. Alle markierten Elemente werden an eine String-Variable angehängt - und zwar in der Form <Key>¦<Text>;.

    Wozu das alles? Ganz einfach: Die Parameterliste der Ereignisprozedur enthält ein Objekt namens Data, das zunächst mit der Clear-Methode geleert und dann über die SetData-Methode mit der soeben zusammengestellten Liste gefüllt wird. Dieses Objekt (OLE-DataObject) ist eine Art Zwischenablage für Drag-and-Drop-Operationen und wird im Ziel-Steuerelement ausgelesen.

    Quellcode 4: ... und endet im ListView lvwZugeordnet.

    Private Sub lvwZugeordnet_OLEDragDrop(Data As Object, Effect As Long, Button As Integer, _
        Shift As Integer, x As Single, y As Single)

        Dim strData As String

        Dim strDataItems() As String

        Dim strText As String

        Dim strKey As String

        Dim i As Integer

        strData = Data.GetData(ccCFText)

        If InStr(1, strData, "¦") <> 0 Then

            If Right(strData, 1) = ";" Then

                strData = Left(strData, Len(strData) - 1)

            End If

            strDataItems = Split(strData, ";")

            For i = 0 To UBound(strDataItems)

                strKey = Split(strDataItems(i), "¦")(0)

                strText = Split(strDataItems(i), "¦")(1)

                If IsNull(DLookup("EmpfaengerID", "tblVerteiler", "PublikationID = " _
                    & Me.PublikationID & " AND EmpfaengerID = " & Mid(strKey, 2))) Then

                    lvwZugeordnet.ListItems.Add , strKey, strText

                    lvwNichtZugeordnet.ListItems.Remove strKey

                    CurrentDb.Execute "INSERT INTO tblVerteiler(PublikationID, EmpfaengerID) " _
                        & "VALUES(" & Me.PublikationID & ", " & Mid(strKey, 2) & ")"

                End If

            Next i

        End If

    End Sub

    Dies passiert dann in der OLEDragDrop-Ereignisprozedur des Ziel-ListViews (s. Quellcode 4): Hier taucht das Objekt Data erneut in der Parameterliste auf. Die GetData-Methode gibt den Inhalt dieses Objekts preis. In der String-Variablen strData angekommen, werden die Daten direkt weiterverarbeitet: Nach der Prüfung, ob mindestens ein "Pipe"-Zeichen (¦) in der übergebenen Zeichenkette enthalten ist, teilen die folgenden Anweisungen die Zeichenkette zunächst in die durch Semikola (;) getrennten Bestandteile auf und speichern sie in einem String-Array. Dieses wird in einer For Next-Schleife über alle Elemente durchlaufen, wobei die Split-Anweisung die Elemente vor und hinter dem "Pipe"-Zeichen in die Variablen strKey und strText extrahiert. Und damit lässt sich natürlich leicht ein neues Element zum ListView hinzufügen. Gleiches gilt auch andersherum: Nachdem die Add-Methode der ListItems-Auflistung des Ziel-ListViews das neue Element hinzugefügt hat, entfernt die Remove-Methode selbiges aus dem Ursprungs-ListView.

    Mit diesen beiden Routinen können Sie nun schon Elemente von rechts nach links, aber noch nicht von links nach rechts ziehen. Das lässt sich aber leicht nachholen: Legen Sie einfach die passenden Ereignisprozeduren für das jeweils andere ListView-Steuerelement an.

    Beim Probieren fällt allerdings eines auf: Nach ein paar Drag-and-Drop-Operationen und einem Wechsel zu einem anderen Publikations-Datensatz im Formular und wieder zurück enthält jedes ListView wieder die ursprünglichen Elemente. Das liegt daran, dass die Datenbankseite bisher außen vor gelassen wurde: Sie können zwar lustig Elemente zwischen den ListViews hin- und herschieben, aber die entsprechenden Änderungen an der Verknüpfungstabelle tblVerteiler erfolgen nicht.

    Der Aufwand hierfür ist allerdings vergleichsweise gering: Für das Hinzufügen eines Kontakts zur Liste der Empfänger muss man schließlich nur einen Datensatz mit der PublikationID und der EmpfaengerID zur Tabelle tblVerteiler hinzufügen; zum Entfernen des Empfängers aus der Liste löscht man einfach den passenden Eintrag aus der Tabelle tblVerteiler.

    Ein Platz für die SQL-Anweisungen ist auch schnell gefunden: Da die Änderungen jeweils für alle markierten Elemente vorzunehmen sind, gehört die entsprechende Anweisung selbstverständlich in eine der For Next-Schleifen, die alle zu bewegenden Elemente durchläuft. Soll ein Eintrag aus dem Verteiler entfernt werden, fügen Sie diese Anweisung in die Routine lvwNichtZugeordnet_OLEDragDrop ein (in einer Zeile):

    CurrentDb.Execute "DELETE FROM 
    tblVerteiler WHERE PublikationID = " & Me.PublikationID & " AND EmpfaengerID = " & Mid(strKey, 2)

    Die entsprechende Anweisung in der Routine lvwZugeordnet_OLEDragDrop sieht so aus:

    CurrentDb.Execute "INSERT INTO 
    tblVerteiler(PublikationID, EmpfaengerID) VALUES(" & Me.PublikationID & ", " & Mid(strKey, 2) & ")"

    Reihenfolge per Drag and Drop

    Neben dem Bewegen von Elementen zwischen zwei ListView-Steuerelementen ist auch das Einstellen der Reihenfolge von Elementen ein häufig gefragtes Thema. Dazu ist zunächst ein Feld in der Herkunftstabelle vorzusehen, das einen die Reihenfolge festlegenden Wert enthält - typischerweise heißt dieses Feld dann auch Reihenfolge.

    Wichtig ist die Überlegung, wann die Änderungen der Reihenfolge im ListView in die zugrunde liegende Tabelle übertragen werden.

    Wenn man dies nach jeder Änderung erledigt, ist schon eine Menge Code notwendig. Daher beschränkt sich dieses Beispiel auf das Übertragen der Änderungen der Reihenfolge auf das Schließen des Formulars - das heißt, dass dann einmalig alle Elemente des ListViews durchlaufen und ihre neue Reihenfolge im Feld Reihenfolge der entsprechenden Tabelle vermerkt wird.

    Sie haben das Ende des frei verfügbaren Teils des Artikels erreicht. Lesen Sie weiter, um zu erfahren, wie Sie den vollständigen Artikel lesen und auf viele hundert weitere Artikel zugreifen können.

    Sind Sie Abonnent?Jetzt einloggen ...
     

    Kompletten Artikel lesen?

    Einfach für den Newsletter anmelden, dann lesen Sie schon in einer Minute den kompletten Artikel und erhalten die Beispieldatenbanken.

    E-Mail:

    © 2003-2015 André Minhorst Alle Rechte vorbehalten.