Kategorien per TreeView verwalten

Kategorien und untergeordnete Kategorien sind ein gutes Beispiel für Daten aus reflexiven Tabellen. Das perfekte Steuer-element für die Verwaltung solcher Daten ist das TreeView-Steuerelement. In diesem Artikel schauen wir uns an, wie Sie die Kategorien einer Datenbank im TreeView-Steuerelement anzeigen und wie Sie dem Benutzer ermöglichen, diese zu verwalten – also neue Kategorien hinzuzufügen und bestehende Kategorien zu löschen, umzubenennen oder anderen Kategorien unterzuordnen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 2001_KategorienPerTreeView.accdb.

Tabellen zum Verwalten der Kategorien

Wenn Sie mit Daten in einer reflexiven Beziehung arbeiten wollen, müssen Sie entscheiden, ob Sie das Feld zum Herstellen der Hierarchie direkt in der Tabelle mit den Daten unterbringen oder ob Sie eine zusätzliche Tabelle zum Herstellen der reflexiven Beziehung integrieren. Wie dies gelingt, zeigen wir im Artikel Reflexive Daten in Tabellen. Im Artikel Reflexive Daten in Abfragen zeigen wir dann, dass mit einer Abfrage auch die Daten aus einer Tabelle mit einer Hilfstabelle zur Abbildung der reflexiven Beziehung in der gleichen Form wie in der Tabelle mit integrierter reflexiver Beziehung dargestellt werden können.

Wir verwenden an dieser Stelle die im erstgenannten Artikel beschriebenen Tabellen tblKategorien2 und tblKategoriezuordnungen als Datenquelle zum Füllen des TreeView-Steuerelements – allein deshalb, weil hier etwas andere Schritte zum Verwalten der Daten des TreeView-Steuerelements nötig sind als bei einer einzelnen Tabelle. Und die Vorgehensweise bei einer einzelnen Tabelle wurden bereits an anderer Stelle dokumentiert. Die Quelle zum Füllen unseres TreeView-Steuerelements beziehen wir aus der Abfrage qryKategorien aus Bild 1, welche die Daten der beiden Tabellen tblKategorien2 und tblKategoriezuordnungen so zusammenfasst, also ob diese aus einer einzigen Tabelle stammen, welche die reflexive Beziehung über ein eigenes Feld herstellt.

Datenbasis für das TreeView-Steuerelement

Bild 1: Datenbasis für das TreeView-Steuerelement

Formulareinstellungen

Das Formular, in dem wir das TreeView-Steuerelement anlegen wollen, nennen wir frmKategorienTreeView. Die Eigenschaften Datensatzmarkierer, Navigationsschaltflächen, Bildlaufleisten und Trennlinien stellen wir allesamt auf Nein und Automatisch zentrieren auf Ja ein.

TreeView hinzufügen

Um das TreeView-Steuerelement zu einem neuen, leeren Formular hinzuzufügen, wählen Sie aus dem Ribbon den Eintrag Entwurf|Steuerelemente|ActiveX-Steuerelemente aus. Im Dialog ActiveX-Steuerelement einfügen selektieren Sie dann den Eintrag Microsoft TreeView Control, version 6.0 und klicken auf OK (siehe Bild 2).

Einfügen des TreeView-Steuerelements

Bild 2: Einfügen des TreeView-Steuerelements

Das TreeView-Steuerelement wird dann ganz klein im linken oberen Bereich des Formularentwurfs eingefügt. Also passen Sie zunächst die Größe an und stellen dann die Verankern-Eigenschaften so ein, dass sich das Steuer-element entsprechend Ihren Wünschen mit der änderung der Größe des Formulars anpasst. Wir wählen es so, dass sich die Höhe des TreeView-Steuerelements mit dem Formular ändert und legen dazu für die Eigenschaft Vertikaler Anker den Wert Beide fest (siehe Bild 3). Außerdem stellen wir den Namen des neuen Steuerelements auf ctlTreeView ein.

Das Formular mit dem jungfräulichen TreeView-Steuerelement

Bild 3: Das Formular mit dem jungfräulichen TreeView-Steuerelement

Die übrigen benötigten Eigenschaften des TreeView-Steuerelements stellen wir per VBA beim Laden des Formulars ein. Dort erledigen wir auch das Füllen mit den Daten der Abfrage qryKategorien.

ImageList-Steuerelement hinzufügen

Wir wollen ein optisch ansprechendes TreeView-Steuerelement erzeugen, also soll dieses auch Icons für die einzelnen Einträge anzeigen. Dazu benötigen wir noch ein Image-List-Steuerelement, welches die Bilder für das TreeView-Steuerelement verwaltet. Das ImageList-Steuerelement fügen wir wie das TreeView-Steuerelement zum Formular hinzu. Sie können es an Ort und Stelle belassen, da man es nach dem Anzeigen des Formulars in der Formularansicht ohnehin nicht sieht. Das Steuer-element erhält den Namen ctl-ImageList. Klicken Sie dann doppelt auf das Steuer-element, um ein Bild hinzuzufügen.

Auf der ersten Seite des nun erscheinenden Eigenschaften-Dialog stellen Sie die Option zur Auswahl der Bildgröße auf 16 x 16 ein (siehe Bild 4).

Einstellungen für das ImageList-Steuerelement

Bild 4: Einstellungen für das ImageList-Steuerelement

Auf der zweiten Seite klicken Sie auf Insert Picture und wählen im nun erscheinenden Dialog die gewünschte Bilddatei aus. Wir verwenden eine .ico-Datei namens Folder.ico.

Nach dem Einfügen legen wir noch die Eigenschaft Key für dieses Bild auf folder fest (siehe Bild 5).

Hinzufügen einer Bilddatei

Bild 5: Hinzufügen einer Bilddatei

ImageList-Steuerelement zuweisen

Das Zuweisen des ImageList-Steuerelements zum TreeView-Steuerelement erledigen wir über den Eigenschaften-Dialog des TreeView-Steuerelements. Diesen öffnen Sie über den Kontextmenü-Eintrag TreeCtl-Object|Properties des TreeView-Steuerelements im Entwurf des Formulars.

Hier finden Sie die Eigenschaft ImageList vor. Für diese wählen wir den Namen des von uns hinzugefügten ImageList-Steuerelements ctlImageList aus (siehe Bild 6).

Festlegen des ImageList-Steuerelements

Bild 6: Festlegen des ImageList-Steuerelements

Weitere TreeView-Eigenschaften einstellen

Die übrigen Eigenschaften stellen wir per VBA in der Ereignisprozedur ein, die durch das Ereignis Beim Laden des Formulars ausgelöst wird. Das Ergebnis sieht wie folgt aus:

Dim WithEvents objTreeView As MSComctlLib.TreeView
Private Sub Form_Load()
    Set objTreeView = Me!ctlTreeView.Object
    With objTreeView
        .Appearance = ccFlat
        .BorderStyle = ccNone
        .OLEDragMode = ccOLEDragAutomatic
        .OLEDropMode = ccOLEDropManual
        .Style = tvwTreelinesPlusMinusPictureText
        .LineStyle = tvwRootLines
    End With
    TreeViewFuellen
End Sub

Die mit dem Schlüsselwort WithEvents deklarierte Variable objTreeView dient dem Referenzieren des TreeView-Steuerelements und für das spätere Anlegen von Ereignisprozeduren für dieses Steuer-element. Die Form_Load-Prozedur weist dieser Variablen zunächst das Steuerelement zu, was nicht einfach über den Namen des Steuerelements geschieht – bei ActiveX-Steuerelementen ist zusätzlich die Angabe der Eigenschaft Object nötig.

Die Prozedur stellt einige Eigenschaften des Steuerelements ein, zum Beispiel das Aussehen auf Flat statt 3d, die Rahmenart, das Verhalten bei Drag and Drop, den Stil (Plus-/Minuszeichen, Icons und Text) sowie den Stil der Linien. Wenn Sie die Unterschiede sehen wollen, können Sie mit diesen Eigenschaften experimentieren.

TreeView füllen

Außerdem ruft diese Prozedur die Routine TreeViewFuellen auf. Diese finden Sie in Listing 1. Die Prozedur leert zunächst das TreeView-Steuerelement mit der Clear-Methode der Nodes-Auflistung.

Private Sub TreeViewFuellen()
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Set db = CurrentDb
     objTreeView.Nodes.Clear
     Set rst = db.OpenRecordset("SELECT * FROM qryKategorien WHERE ParentKategorieID IS NULL", dbOpenDynaset)
     Do While Not rst.EOF
         objTreeView.Nodes.Add , , "k" & rst!kategorieID, rst!Kategorie, "folder"
         TreeViewFuellen_Rek db, rst!kategorieID
         rst.MoveNext
     Loop
End Sub

Listing 1: Füllen der ersten Ebene des TreeView-Steuerelements

Dann füllt es ein Recordset-Objekt mit allen Datensätzen der Abrage qryKategorien, deren Feld ParentKategorieID leer ist. Damit erhalten wir alle Datensätze der ersten Ebene, also diejenigen, die keine übergeordneten Kategorien aufweisen. Diese durchläuft die Prozedur in einer Do While-Schleife.

Innerhalb der Schleife geschehen zwei Dinge:

  • Es wird ein Element im TreeView-Steuerelement für diesen Datensatz angelegt. Dabei übergibt die Prozedur der Add-Methode der Nodes-Auflistung des TreeView-Steuerelements einen eindeutigen Bezeichner, der aus dem Buchstaben a und dem Primärschlüsselwert des Datensatzes besteht, den anzuzeigenden Text aus dem Feld Kategorie sowie den Key des für dieses Element anzuzeigenden Icons aus dem ImageList-Steuerelements – hier folder.
  • Außerdem wird die Prozedur TreeViewFuellen_Rek aufgerufen, die sich um das Füllen der untergeordneten Elemente kümmert (siehe Listing 2).
  • Private Sub TreeViewFuellen_Rek(db As DAO.Database, lngParentID As Long)
         Dim rst As DAO.Recordset
         Set rst = db.OpenRecordset("SELECT * FROM qryKategorien WHERE parentKategorieID = " & lngParentID, dbOpenDynaset)
         Do While Not rst.EOF
             objTreeView.Nodes.Add "k" & lngParentID, tvwChild, "k" & rst!kategorieID, rst!Kategorie, "folder"
             TreeViewFuellen_Rek db, rst!kategorieID
             rst.MoveNext
         Loop
    End Sub

    Listing 2: Füllen der weiteren Ebenen des TreeView-Steuerelements

Untergeordnete Elemente hinzufügen

Die Prozedur TreeViewFuellen_Rek erwartet mit db einen Verweis auf das Database-Objekt der aktuellen Datenbank sowie mit lngParentID den Primärschlüsselwert des Elements, dessen untergeordnete Elemente in dieser Prozedur angelegt werden sollen.

Die Prozedur durchläuft wiederum die Datensätze eines Recordsets. Diesmal enthält das Recordset jedoch nicht alle Datensätze, die kein übergeordnetes Element enthalten, sondern diejenigen, deren übergeordnetes Element das mit lngParentID übergebene ist. Innerhalb der folgenden Do While-Schleife wird jeweils ein Element dem TreeView-Steuerelement hinzugefügt.

Während wir in der Prozedur TreeViewFuellen die ersten beiden Parameter der Add-Methode der Nodes-Auflistung leer lassen konnten, müssen wir diese nun füllen, um die Verwandschaftsbeziehung zwischen den Elementen anzugeben. Also füllen wir den ersten Parameter mit dem Key-Wert des Elements, dem wir das neue Element unterordnen wollen. Diesen Keywert stellen wir aus dem Buchstaben a und dem mit lngParentID gelieferten Primärschlüsselwert des übergeordneten Elements zusammen. Der zweite Parameter gibt das Verwandtschaftsverhältnis an, in diesem Fall tvwChild für ein untergeordnetes Element. Die übrigen Parameter füllen wir wie bereits für die aufrufende Prozedur beschrieben.

Danach rufen wir erneut die Prozedur TreeViewFuellen_Rek auf. Diese ruft sich erst dann nicht mehr selbst auf, wenn das ermittelte Recordset der untergeordneten Elemente leer ist. Auf diese Weise füllen wir das TreeView-Steuerelement, das anschließend wie in Bild 7 aussieht.

Aussehen des TreeView-Steuerelements nach dem öffnen

Bild 7: Aussehen des TreeView-Steuerelements nach dem öffnen

Klappen wir die untergeordneten Elemente per Mausklick auf die Plus-Zeichen auf, erhalten wir das TreeView-Steuerelement aus Bild 8.

Aussehen des TreeView-Steuerelements mit aufgeklappten Unterelementen

Bild 8: Aussehen des TreeView-Steuerelements mit aufgeklappten Unterelementen

Elemente hinzufügen

Wenn Sie nun ein Element hinzufügen wollen, finden Sie keine eingebaute Funktion für diesen Zweck vor – wir müssen diese komplett selbst erzeugen. Hierbei unterscheiden wir zwischen zwei Fällen:

  • Sie erzeugen ein Element der obersten Ebene oder
  • Sie erzeugen ein Element, das einem bereits vorhandenen Element untergeordnet wird.

Beides wollen wir über Kontextmenüs erledigen, weil dies die ergonomischste Variante ist. Um mit Kontextmenüs zu arbeiten, benötigen wir einen Verweis auf die Bibliothek Microsoft Office x.0 Object Library, die unter Access 2016 im Verweise-Dialog (VBA-Editor, Menüeintrag Extras|Verweise) unter office abgelegt ist (siehe Bild 9).

Hinzufügen eines Verweises auf die Office-Bibliothek

Bild 9: Hinzufügen eines Verweises auf die Office-Bibliothek

Dann fügen wir eine Prozedur hinzu, die beim Anklicken des TreeView-Steuerelements mit der Maus ausgelöst wird. Diese Prozedur wird durch das Ereignis MouseDown ausgelöst. Die passende Ereignisprozedur legen wir an, indem Sie im Codefenster der Klasse Form_frmKategorienTreeView im linken Kombinationsfeld den Eintrag ctlTreeView auswählen und im rechten den Eintrag MouseDown.

Dies erstellt dann automatisch die gewünschte Ereignisprozedur, die wir dann wie in Listing 3 füllen.

Private Sub ctlTreeView_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Long, ByVal y As Long)
      Dim strTyp As String
      Dim lngID As Long
      Dim strText As String
      Dim objNode As MSComctlLib.Node
      Select Case Button
          Case acRightButton
              Set objNode = objTreeView.HitTest(x, y)
              If Not objNode Is Nothing Then
                  strTyp = Left(objNode.Key, 1)
                  lngID = Mid(objNode.Key, 2)
                  strText = objNode.Text
                  Select Case strTyp
                      Case "k"
                          KontextmenueKategorieAnzeigen lngID, strText
                  End Select
              Else
                  KontextmenueRootAnzeigen
              End If
      End Select
End Sub

Listing 3: Aufrufen von Kontextmenüs

Wir wollen, dass beim Anklicken eines der vorhanden Einträge ein Kontextmenü angezeigt wird, dass die folgenden Befehle anzeigt:

  • Kategorie einfügen
  • Kategorie löschen

Beim Anklicken des leeren Raumes im TreeView-Steuerelement soll ein Kontextmenü mit dem Befehl Kategorie einfügen anzeigen. Wir benötigen also zwei Kontextmenüs.

Um dies zu erledigen, prüfen wir in der Prozedur ctlTreeView_MouseDown zunächst, mit welcher Maustaste der Benutzer das TreeView-Steuerelement angeklickt hat. Nur im Falle der rechten Maustaste sollen die weiteren Anweisungen der Prozedur ausgeführt werden, was wir durch eine entsprechende If…Then-Bedingung absichern. Hat der Benutzer das TreeView-Steuerelement mit der rechten Maustaste angeklickt, untersuchen wir, ob er ein Element angeklickt hat oder den freien Raum im TreeView-Steuerelement. Dazu führen wir die HitTest-Funktion aus und übergeben die mit den Parametern x und y mitgegebene Position des Mausklicks als Parameter. Hat der Benutzer einen der Einträge angeklickt, ist objNode anschließend mit einem Verweis auf diesen Eintrag gefüllt.

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins Access [basics]:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 400 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar