Im ersten Teil dieser Beitragsreihe haben wir ein ListView-Steuerelement mit den Daten aus einer Mitarbeitertabelle gefüllt, Icons hinzugefügt und eine Sortierfunktion implementiert. Im vorliegenden Beitrag bauen wir dieses Beispiel weiter aus: Wir zeigen, wie man dem ListView-Steuerelement neue Einträge hinzufügen und bestehende Einträge bearbeiten oder löschen kann. Außerdem erklären wir, wie man die Elemente auch nach numerischen Daten oder nach Datumsfeldern korrekt sortiert. Schließlich fügen wir noch ein Kontextmenü hinzu, mit dem man den Status der Mitarbeiter anpassen kann.
Rückblick auf Teil 1
Im ersten Teil dieser Beitragsreihe (www.access-im-unternehmen.de/1574) haben wir zunächst eine Abfrage namens qryMitarbeiter erstellt, die alle benötigten Lookup-Tabellen – tblAnreden, tblAbteilungen und tblStatus – einbindet. Anschließend haben wir das ListView-Steuerelement in der Ereignisprozedur Form_Load eingerichtet und mit Spaltenüberschriften versehen.
Die Prozedur ListViewFuellen liest die Datensätze per Recordset aus und fügt sie als ListItem– und ListSubItem-Objekte ein. Außerdem haben wir per Windows-API-Aufruf die Spaltenbreiten automatisch an Inhalt und Überschrift angepasst, für jeden Mitarbeiter ein statusabhängiges Icon aus der Tabelle MSysResources eingeblendet und eine einfache Sortierfunktion per Klick auf den Spaltenkopf ergänzt. Bild 1 zeigt den Stand am Ende von Teil 1.

Bild 1: Aktueller Stand des ListView-Steuerelements
Sortierung nach Zahlen- und Datumsfeldern
Im ersten Teil haben wir bereits eine Sortierfunktion eingebaut, die über einen Klick auf den jeweiligen Spaltenkopf ausgelöst wird. Dabei zeigte sich jedoch ein Problem: Zahlen und Datumsangaben werden lexikografisch sortiert, also wie Zeichenketten.
Das führt dazu, dass beispielsweise der Wert 10 vor dem Wert 2 erscheint und Datumsangaben nicht chronologisch, sondern nach ihrem Textwert geordnet werden.
Um das zu beheben, ersetzen wir die eingebaute Sortierfunktion durch eigene Prozeduren, die Zahlen- und Datumswerte korrekt vergleichen.
Als Basis dienen die API-Konstanten und die SendMessage-Deklaration aus Listing 1, die wir im allgemeinen Modul mdlListViewTabellen2 ablegen.
Private Const LVM_FIRST As Long = &H1000 Private Const LVM_SORTITEMSEX As Long = LVM_FIRST + 81 #If VBA7 Then Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As LongPtr, _ ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr #Else Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long #End If
Listing 1: Benötigte API-Deklarationen
Für die eigentliche Sortierung erweitern wir die Ereignisprozedur ctlListView_ColumnClick aus Teil 1.
Nach dem Setzen von SortKey und SortOrder rufen wir eine eigene Prozedur ListViewSortieren auf, die den Vergleichstyp je Spalte festlegt (siehe Listing 2).
Private Sub ctlListView_ColumnClick(ByVal ColumnHeader As Object) Static intAktuelleSortierspalte As Integer Static bolAufsteigend As Boolean Dim objListView As MSComctlLib.ListView Set objListView = Me.ctlListView.Object objListView.SortKey = ColumnHeader.Index - 1 If intAktuelleSortierspalte = ColumnHeader.Index - 1 Then bolAufsteigend = Not bolAufsteigend objListView.SortOrder = Abs(bolAufsteigend) Else objListView.SortOrder = lvwAscending End If intAktuelleSortierspalte = ColumnHeader.Index - 1 Call ListViewSortieren(objListView, ColumnHeader.Index - 1, bolAufsteigend) End Sub
Listing 2: Angepasste ColumnClick-Prozedur
Die Prozedur ListViewSortieren im Modul mdlListViewTabellen2 verzweigt per Select Case je nach Spaltenindex in die passende Sortierprozedur: Index 1 (ID) landet in ListViewSortNumerisch, Index 7 (Geburtstag) in ListViewSortDatum, alle anderen Spalten nutzen weiterhin die eingebaute Textsortierung über objLV.Sorted = True (siehe Listing 3).
Public Sub ListViewSortieren(objLV As MSComctlLib.ListView, intSpalte As Integer, bolAufsteigend As Boolean) Select Case intSpalte Case 1 'ID: numerisch Call ListViewSortNumerisch(objLV, intSpalte, bolAufsteigend) Case 7 'Geburtstag: Datum Call ListViewSortDatum(objLV, intSpalte, bolAufsteigend) Case Else 'alle anderen: Text objLV.Sorted = True End Select End Sub
Listing 3: Verzweigung nach Spaltentyp
Beide Sortierprozeduren arbeiten nach dem gleichen Prinzip: Sie vergleichen im Bubblesort-Durchlauf die Werte zweier benachbarter Zeilen direkt und tauschen bei Bedarf sofort deren Inhalte. Da die Index-Eigenschaft eines ListItem-Objekts schreibgeschützt ist, können wir die Reihenfolge der Einträge nicht durch Umsetzen von Indizes verändern. Stattdessen lassen wir die ListItem-Objekte an ihrer Position und schreiben nur die angezeigten Inhalte um – das Ergebnis ist dasselbe.
Routine zum Vertauschen von ListView-Einträgen
Den Austausch übernimmt die private Hilfsprozedur ListViewZeilenTauschen. Sie erhält das ListView-Objekt sowie die Indizes der beiden zu tauschenden Zeilen. Zunächst speichert sie den Text der ersten Spalte von Zeile A in einer Hilfsvariablen, kopiert den Wert aus Zeile B nach A und schreibt den gespeicherten Wert nach B – der klassische Drei-Wege-Tausch. Anschließend wiederholt sie diesen Vorgang für jedes ListSubItem der Zeile. Da im ListView-Steuerelement auch das Icon je Zeile gespeichert ist, tauscht die Prozedur zuletzt noch den SmallIcon-Index, damit das Icon beim Umsortieren mitwandert und weiterhin zum jeweiligen Datensatz passt (siehe Listing 4).
Private Sub ListViewZeilenTauschen(objLV As MSComctlLib.ListView, lngA As Long, lngB As Long) Dim strTemp As String Dim i As Long strTemp = objLV.ListItems(lngA).Text objLV.ListItems(lngA).Text = objLV.ListItems(lngB).Text objLV.ListItems(lngB).Text = strTemp For i = 1 To objLV.ListItems(lngA).ListSubItems.Count strTemp = objLV.ListItems(lngA).ListSubItems(i).Text objLV.ListItems(lngA).ListSubItems(i).Text = objLV.ListItems(lngB).ListSubItems(i).Text objLV.ListItems(lngB).ListSubItems(i).Text = strTemp Next i 'SmallIcon-Index ebenfalls tauschen Dim lngIconA As Long lngIconA = objLV.ListItems(lngA).SmallIcon objLV.ListItems(lngA).SmallIcon = objLV.ListItems(lngB).SmallIcon objLV.ListItems(lngB).SmallIcon = lngIconA End Sub
Listing 4: Hilfsprozedur zum Tauschen zweier ListView-Zeilen
Sortieren mit Bubblesort
Die Prozedur ListViewSortNumerisch implementiert den Bubblesort mit zwei verschachtelten Schleifen. Die äußere Schleife über i läuft von 1 bis lngAnzahl – 1 und steuert die Durchläufe. Die innere Schleife über j läuft von 1 bis lngAnzahl – i – nach jedem Durchlauf steht der größte noch unsortierte Wert am Ende, weshalb der Vergleichsbereich mit jedem Durchlauf um eine Position kürzer wird. Im Schleifenrumpf lesen wir die Werte der beiden betrachteten Zeilen aus: Bei Spaltenindex 0 steht der Wert in ListItems(j).Text, bei allen anderen Spalten in ListItems(j).ListSubItems(intSpalte).Text. Beide Werte wandeln wir per CLng in ganzzahlige Werte um und vergleichen sie.
Ist die Reihenfolge falsch – bei aufsteigender Sortierung also lngA > lngB, bei absteigender lngA < lngB – rufen wir ListViewZeilenTauschen auf (siehe Listing 5).
Public Sub ListViewSortNumerisch(objLV As MSComctlLib.ListView, intSpalte As Integer, bolAufsteigend As Boolean) Dim i As Long, j As Long Dim lngAnzahl As Long lngAnzahl = objLV.ListItems.Count 'Bubblesort: Zeileninhalt direkt tauschen For i = 1 To lngAnzahl - 1 For j = 1 To lngAnzahl - i Dim lngA As Long, lngB As Long If intSpalte = 0 Then lngA = CLng(objLV.ListItems(j).Text) lngB = CLng(objLV.ListItems(j + 1).Text) Else lngA = CLng(objLV.ListItems(j).ListSubItems(intSpalte).Text) lngB = CLng(objLV.ListItems(j + 1).ListSubItems(intSpalte).Text) End If If bolAufsteigend Then If lngA > lngB Then Call ListViewZeilenTauschen(objLV, j, j + 1) Else If lngA < lngB Then Call ListViewZeilenTauschen(objLV, j, j + 1) End If Next j Next i End Sub
Listing 5: Numerische Sortierung
Die Prozedur ListViewSortDatum ist identisch aufgebaut – der einzige Unterschied ist die Typumwandlung per CDbl(CDate(…)), da Access Datumsangaben intern als Gleitkommazahl speichert (siehe Klassenmodul Form_frmMitarbeiterImListView).
Nach diesen Anpassungen liefert ein Klick auf den Spaltenkopf ID eine korrekte numerische Sortierung, und ein Klick auf Geburtstag eine chronologische.
In Bild 2 sehen wir, dass die Sortierung nach dem Geburtstag nun funktioniert.

Bild 2: Korrekte Sortierung nach dem Feld Geburtstag
Bearbeitungs-Dialog per Doppelklick öffnen
Damit der Benutzer einen Mitarbeiterdatensatz direkt aus dem ListView-Steuerelement heraus bearbeiten kann, öffnen wir per Doppelklick auf einen Eintrag ein Bearbeitungsformular. Das Formular frmMitarbeiterDetail ist an die Tabelle tblMitarbeiter gebunden und enthält alle Felder des Datensatzes (siehe Bild 3).

Bild 3: Das Formular zum Bearbeiten der Mitarbeiterdetails
Die Eigenschaft Automatisch zentrieren stellen wir aus Ja, Bildlaufleisten, Navigationsschaltflächen und Datensatzmarkierer auf Nein und Zyklus auf Aktueller Datensatz ein.
Wir öffnen es mit DoCmd.OpenForm und übergeben die MitarbeiterID des angeklickten Eintrags als Filterargument.
Für das Doppelklick-Ereignis des ListView-Steuerelements hinterlegen wir die Ereignisprozedur aus Listing 6.
Nur für Abonnenten
Ab hier wird’s wirklich spannend – der Rest ist exklusiv für Abonnenten.
Mit dem Abo von Access im Unternehmen bekommst du den kompletten Artikel – inklusive vollständigem Code, Beispieldatenbank und Schritt-für-Schritt-Erklärung.
So sparst du dir stundenlanges Herumprobieren, vermeidest teure Fehler in deiner Access-Anwendung und kannst Lösungen direkt in deinem Unternehmen einsetzen, statt nur darüber zu lesen.
Teste Access im Unternehmen jetzt 4 Wochen lang kostenlos: Voller Zugriff auf alle Artikel, Downloads und Beispieldatenbanken. Kein Risiko – wenn es für dich nicht passt, kündigst du einfach innerhalb der ersten vier Wochen.
Bereits Abonnent? Hier einloggen
Kostenlos & unverbindlich
Oder hast Du eine konkrete Frage zu Deiner eigenen Access-Anwendung?
Vielleicht stellt Deine Anwendung Dich vor eine Herausforderung, zu der Du bisher keine Lösung findest. Schlechte Performance, kein ausreichender Zugriffsschutz, Du bist unsicher über Dein Datenmodell oder Dein Code liefert unerklärliche Fehler?
In unserem kostenlosen Access-Audit schaut sich André Minhorst persönlich gemeinsam mit Dir Deine Lösung per Zoom an – und zeigt Dir, wo Datenmodell, VBA-Code, Ergonomie und Sicherheit Optimierungspotenzial bieten.
Jetzt kostenloses Access-Audit anfordern →