Home > Artikel > Ausgabe 2/2013 > ListView-Steuerelement: Drag and Drop, Teil II

ListView-Steuerelement: Drag and Drop, Teil II

Achtung: Sie sind nicht angemeldet. Wenn Sie Abonnent sind und sich anmelden, lesen Sie den kompletten Artikel, laden das PDF herunter oder probieren die Beispieldatenbank aus (sofern vorhanden).

ListView-Steuerelemente unterstützen im Gegensatz zum Listenfeld sogar den Einsatz von Drag and Drop. Damit können Sie nicht nur Einträge von einem ListView-Steuerelement zum nächsten ziehen, sondern auch die Reihenfolge der Einträge eines ListView-Steuerelements einstellen. Im zweiten Teil dieser Mini-Artikelreihe zeigen wir, wie Sie per Drag and Drop die Reihenfolge von ListView-Einträgen anpassen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1302_ListView.mdb.

Ändern der Reihenfolge per Drag and Drop

In der Datenblattansicht, der Endlosansicht oder in Listenfeldern lässt sich mit viel Trickserei ein Drag-and-Drop-Vorgang nachbauen. Das ist allerdings mit viel Aufwand verbunden. Viel einfacher wird es mit dem ListView-Steuerelement. Dieses liefert von Haus aus grundlegende Drag-and-Drop-Funktionen mit, die Sie nur noch ausprogrammieren müssen.

Und damit lassen sich natürlich nicht nur Einträge von einem ListView-Steuerelement zum nächsten bewegen, wie im ersten Teil dieser Artikelserie beschrieben, sondern Sie können damit auch die Reihenfolge der Elemente eines einzelnen ListView-Steuerelements einstellen.

Beispieldaten

Das ListView-Steuerelement soll das Sortieren der Datensätze der Tabelle tblKunden nach dem Wert eines neuen Feldes namens ReihenfolgeID ermöglichen. Dieses Feld fügen wir der entsprechenden Tabelle der Beispieldatenbank hinzu; es erhält den Datentyp Zahl (siehe Bild 1).

Vorbereitung der Tabelle tblKunden

Bild 1: Vorbereitung der Tabelle tblKunden

Dieses Feld müssen wir noch initial füllen, was wir gleich beim Öffnen des Formulars erledigen. Damit ist auch sichergestellt, dass zwischenzeitlich hinzugefügte Felder oder entfernte Felder berücksichtigt werden.

Beispielformular

Das Beispielformular heißt frmListView­Drag­Drop­Rei­hen­folge. Fügen Sie diesem zunächst ein einfaches ListView-Steuerelement hinzu (Steuer­ele­mente|ActiveX-Steuerelemente, dort Microsoft List­View Control 6.0 (SP6) auswählen). Benennen Sie dieses Steuerelement mit lvwKunden.

Nun sorgen Sie dafür, dass das ListView-Steuerelement beim Laden des Formulars mit den gewünschten Daten gefüllt wird – allerdings nicht, ohne den Wert des Feldes ReihenfolgeID zuvor anzupassen.

Doch zunächst zu der Prozedur, die beim Laden des Formulars ausgelöst wird. Diese legen Sie durch Auswählen des Eintrags [Ereignisprozedur] für die Eigenschaft Beim Laden des Formulars und anschließendes Anklicken der Schaltfläche mit den drei Punkten (...) an.

Die nun erscheinende Prozedur, die gleichzeitig mit dem Klassenmodul Form_frmListViewDragDropReihenfolge angelegt wird, füllen Sie wie in Listing 1 abgebildet. Damit die dort verwendete Variable objLvwKunden, mit der die Prozedur auf das ListView-Steuerelement verweist, bekannt ist, deklarieren Sie diese in einer eigenen Zeile im Kopf des Moduls – also gleich unter den beiden Option ...-Zeilen:

Private Sub Form_Load()

     Call ReihenfolgeAktualisieren

     Set objLvwKunden = Me!lvwKunden.Object

     With objLvwKunden

         .View = lvwReport

         .Appearance = ccFlat

         .BorderStyle = ccNone

         .FlatScrollBar = False

         .ColumnHeaders.Clear

         .ColumnHeaders.Add , , "Kunde", Me!lvwKunden.Width

         .OLEDragMode = ccOLEDragAutomatic

         .OLEDropMode = ccOLEDropManual

     End With

     KundenFuellen

End Sub

Listing 1: Einrichten und Füllen des ListView-Steuerelements

Dim WithEvents objLvwKunden As MSComctlLib.ListView

Das Schlüsselwort WithEvents verwenden wir, damit wir im Klassenmodul des Formulars ganz einfach Ereignisprozeduren anlegen können, die durch das ListView-Steuerelement ausgelöst werden. Dies ist für die Programmierung der Drag-and-Drop-Funktionalität wichtig.

Die Prozedur ruft zunächst die Prozedur ReihenfolgeAktualisieren auf, die sich um das Füllen des Feldes ReihenfolgeID kümmert – mehr dazu weiter unten.

Danach füllt sie die Variable objLvwKunden mit einem Verweis auf das entsprechende Steuerelement. Anschließend stellt es einige Eigenschaften dieses Steuerelements ein. Dies könnten Sie auch über den Eigenschaften-Dialog des Steuerelements durchführen, aber wir erledigen das per Code.

Neben einigen optischen Einstellungen sorgt die Methode Add der ColumnHeaders-Auflistung für das Anlegen einer einzigen Spalte, welche später die Werte des Feldes Firma der Kundendatensätze aufnehmen soll. Da dies die einzige Spalte ist, stellt die Prozedur die Spaltenbreite auf die Breite des Steuerelements ein. Schließlich folgen zwei Einstellungen, die für die Programmierbarkeit von Drag and Drop wichtig sind: OLEDragMode und OLEDropMode.

Den Abschluss macht ein Aufruf der Prozedur KundenFuellen, die schließlich die Kundennamen in das ListView-Steuerelement füllt.

Reihenfolge aktualisieren

Die Prozedur ReihenfolgeAktualisieren aus Listing 2 verwendet die Abfrage qryKundenNachReihenfolgeID als Basis eines Recordsets. Die Prozedur durchläuft alle Datensätze dieser Abfrage in einer Do While-Schleife und bearbeitet dabei den jeweils aktuelle Datensatz, indem sie den Wert des Feldes ReihenfolgeID auf einen Wert einstellt, welcher der Eigenschaft AbsolutePosition des aktuellen Recordsets plus eins entspricht. Der Wert 1 wird deshalb addiert, weil AbsolutePosition für den ersten Datensatz den Wert 0 liefert, die Werte des Feldes ReihenfolgeID jedoch mit 1 beginnen sollen.

Public Sub ReihenfolgeAktualisieren()

     Dim db As DAO.Database

     Dim rst As DAO.Recordset

     Set db = CurrentDb

     Set rst = db.OpenRecordset("SELECT * FROM qryKundenNachReihenfolgeID", dbOpenDynaset)

     Do While Not rst.EOF

         rst.Edit

         rst!ReihenfolgeID = rst.AbsolutePosition + 1

         rst.Update

         rst.MoveNext

     Loop

     Set db = Nothing

End Sub

Listing 2: Einstellen der ReihenfolgeID der enthaltenen Datensätze

Zum Einstellen dieses Wertes versetzt die Prozedur den Datensatz mit der Edit-Methode in den Bearbeitungsmodus, ändert den Wert des Feldes ReihenfolgeID und speichert die Änderung mit der Update-Methode. Die MoveNext-Methode verschiebt den Datensatzzeiger auf den nächsten Datensatz. Dieser Vorgang wird solange wiederholt, bis die Eigenschaft EOF des Recordset-Objekts den Wert True annimmt – der Datensatzzeiger befindet sich somit hinter dem letzten Datensatz.

Interessant ist die Abfrage qryKundenNachReihenfolgeID, welche festlegt, in welcher Reihenfolge die Kundendatensätze tatsächlich nummeriert werden. Die Abfrage finden Sie in Bild 2. Sie basiert auf der Tabelle tblKunden und enthält drei Felder, von denen nur das Feld ReihenfolgeID angezeigt wird. Das zweite und das dritte Feld dienen der korrekten Sortierung vor dem Aktualisieren der Reihenfolge.

Abfrage zum Einstellen der ReihenfolgeID der Kundendatensätze

Bild 2: Abfrage zum Einstellen der ReihenfolgeID der Kundendatensätze

Wichtig ist, dass diese Abfrage die Reihenfolge der vorhandenen Kundendatensätze nicht durcheinander bringt und dass neue Datensätze hinten eingereiht werden.

Daher sortiert die Abfrage die Datensätze zunächst nach dem Feld ReihenfolgeID und erst danach nach dem Feld KundeID. Würde man dies so belassen, würde die Abfrage jedoch zuerst die neu hinzugefügten Datensätze liefern. Diese weisen nämlich den Wert Null im Feld ReihenfolgeID auf und sind in der Reihenfolge vor den mit Zahlenwerten gefüllten Feldern zu finden.

Also müssen wir dafür sorgen, dass der Wert Null durch einen Wert ersetzt wird, der größer als die bisher vergebenen Werte im Feld ReihenfolgeID ist. Da die Sortierung per Drag and Drop ohnehin nur für eine überschaubare Anzahl Datensätze sinnvoll ist, gehen wir davon aus, dass der Wert 999.999 ausreichend ist. Der folgende Ausdruck prüft, ob ReihenfolgeID leer ist (also den Wert Null enthält) und fügt in diesem Fall den Wert 999.999 ein:

Reihenfolge: ZLong(Nz([ReihenfolgeID];999999))

Das Ergebnis der Nz-Funktion wird in der Abfrage aber dummerweise als String interpretiert. Sie können sich dies in der Abfrage qryKundenNachReihenfolgeIDFalsch ansehen, die das Ergebnis aus Bild 3 liefert.

Fehlerhafte Interpretation der Ergebnisse von Nz

Bild 3: Fehlerhafte Interpretation der Ergebnisse von Nz

Zumindest erfolgt dort die Sortierung so, als ob es sich bei dem Feld um ein Textfeld handelt – 10 landet so beispielsweise vor 2.

Dies lässt sich allerdings beheben, indem wir das Ergebnis der Nz-Funktion noch mit der CLng-Funktion in einen Long-Wert konvertieren.

Eintragen der ListView-Werte

Zuletzt ruft die Prozedur Form_Load die Routine KundenFuellen auf (siehe Listing 3). Diese erzeugt ein Recordset auf Basis der Tabelle tblKunden, wobei die Reihenfolge durch das soeben frisch gefüllte Feld ReihenfolgeID festgelegt wird.

Private Sub KundenFuellen()

     Dim db As DAO.Database

     Dim rst As DAO.Recordset

     Set db = CurrentDb

     Set rst = db.OpenRecordset("SELECT * FROM tblKunden ORDER BY ReihenfolgeID", dbOpenDynaset)

     objLvwKunden.ListItems.Clear

     Do While Not rst.EOF

         objLvwKunden.ListItems.Add , "a" & rst!KundeID, rst!Firma

         rst.MoveNext

     Loop

     rst.Close

     Set rst = Nothing

     Set db = Nothing

End Sub

Listing 3: Einlesen der Datensätze und Einfügen in das ListView-Steuerelement

Die Clear-Methode der ListItems-Auflistung säubert das ListView-Steuerelement von eventuell vorhandenen Einträgen. Danach durchläuft eine Do While-Schleife alle Datensätze des Recordsets und legt mit der Add-Methode der gleichen Auflistung jeweils ein neues Element je Datensatz an. Dabei vergibt die Methode als Key den Buchstaben a plus dem Primärschlüsselwert und als anzuzeigenden Text den Namen der Firma des aktuellen Datensatzes.

Fertig – das Formular sieht nach dem Öffnen wie in Bild 4 aus.

Das gefüllte ListView-Steuerelement

Bild 4: Das gefüllte ListView-Steuerelement

Drag and Drop

Nun benötigen wir einige Ereignisprozeduren, die durch das Ziehen eines Eintrags mit der Maus ausgelöst werden. Zuvor überlegen wir uns jedoch noch, wie sich das Ziehen und Fallenlassen eines Eintrags überhaupt auswirken soll. Immerhin gibt es da eine Kleinigkeit zu beachten. So können Sie einen Eintrag immer nur auf einen anderen Eintrag ziehen, aber nicht zwischen zwei Einträge. Dementsprechend müssen wir festlegen, wie das ListView-Steuerelement verfahren soll, wenn Sie einen Eintrag auf einen anderen ziehen.

Soll der gezogene Eintrag dann vor dem Zieleintrag eingefügt werden oder hinter diesem Eintrag? Und was geschieht, wenn der Benutzer einen Eintrag aus der Mitte auf den ersten oder den letzten Eintrag zieht? Einigen wir uns auf die folgende Vorgehensweise: Wenn der Eintrag auf einen über ihm liegenden Eintrag gezogen wird, nimmt er die Position über diesem Eintrag ein und wenn der Eintrag auf einen unter ihm liegenden Eintrag gezogen wird, landet er darunter. So lassen sich auch einfach Einträge an die erste und die letzte Position verschieben.

Aktionen beim Drag and Drop

Wenn der Benutzer ein Element auf ein anderes zieht, sollen folgende Aktionen ausgeführt werden:

  • Das gezogene Element soll die Position des Zielelements einnehmen.
  • Wenn das Zielelement über dem gezogenen Element liegt, rutschen das Zielelement und alle darunter liegenden Elemente bis zum vorherigen Platz des gezogenen Elements um eine Position nach unten. Wenn das Zielelement unterhalb des gezogenen Elements liegt, nimmt das gezogene Element ebenfalls den Platz des Zielelements ein. Das Zielelement und alle darüberliegenden Elemente bis zur vorherigen Position des vorherigen Elements rücken um eine Position nach oben.
  • In der Tabelle tblKunden müssen die Werte des Feldes ReihenfolgeID für die betroffenen Elemente angepasst werden.

Start des Drag-and-Drop-Vorgangs

Wenn der Benutzer auf ein Element klickt und die Maus bei gedrückter linker Maustaste bewegt, löst dies das Ereignis OLEStartDrag des Steuerelements objLvwKunden aus. Um dieses zu implementieren, wählen Sie im VBA-Editor im Codefenster des Klassenmoduls Form_frmListViewDragDropReihenfolge im linken Kombinationsfeld den Wert objLvwKunden und im rechten Kombinationsfeld den Eintrag OLEStartDrag aus (siehe Bild 5).

Anlegen des Ereignisses, das beim Start des Drag-and-Drop-Vorgangs ausgelöst wird.

Bild 5: Anlegen des Ereignisses, das beim Start des Drag-and-Drop-Vorgangs ausgelöst wird.

Die auf diese Weise erzeugte Prozedur füllen Sie mit den Anweisungen aus Listing 4. Die dortige Prozedur füllt zunächst die Objektvariable objListItem mit einem Verweis auf das aktuell ausgewählte Element. Dieses kann mit der Funktion Selec­ted­Item ermittelt werden. Dieses Element enthält für die Eigenschaft Key den Wert, der beim Anlegen der Elemente als Parameter übergeben wurde und mit dem Buchstaben a beginnt und den Primärschlüsselwert des entsprechenden Kundendatensatzes enthielt.

Sie haben das Ende des frei verfügbaren Teil dieses Artikels erreicht!

Wenn Sie mehr lesen und auf viele weitere Artikel zugreifen möchten, melden Sie sich als Abonnent unter Login an. Falls nicht, bestellen Sie doch einfach ein Jahresabonnement!