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 1/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

Füllen Sie das Treeview-Steuerelement mit Daten aus einer oder mehreren Tabellen.

Techniken

Formulare, Treeview-Steuerelement, VBA

Voraussetzungen

Access 2002 und höher oder Access 97/2000 und Developer Edition oder Visual Studio 6.0

Treeview-Steuerelement mit Daten füllen

André Minhorst, Duisburg

Das Treeview-Steuerelement eignet sich wie kein anderes zur Anzeige von hierarchischen Daten. Dieser Beitrag zeigt Ihnen, wie Sie es mit Daten aus einer oder mehreren Tabellen füllen und wie Sie mit kleinen Tricks einiges an Performance herausholen können. Dabei kommen nicht nur Daten aus mehreren verknüpften Tabellen zum Zuge, sondern auch solche aus reflexiven Beziehungen.

Von der Tabelle ins
Treeview-Steuerelement

Der Beitrag Das Treeview-Steuerelement (s. Shortlink 319) zeigt, wie Sie die Add-Methode der Nodes-Auslistung eines Treeview-Steuerelements verwenden, um Knoten an einen Baum anzufügen.

Das Ganze macht natürlich nur wenig Sinn, wenn Sie keine Daten haben, die Sie in das Treeview einpflegen können. Die bekannteste Anwendung des Treeview-Steuerelements ist wohl der Windows Explorer. Hier finden Sie auf der linken Seite das Treeview-Steuerelement zur Anzeige der Verzeichnisse und auf der rechten Seite das ListView-Steuerelement für die im aktuell markierten Verzeichnis enthaltenen Unterverzeichnisse und Dateien.

Vermutlich wollen Sie unter Access nicht gerade den Windows Explorer nachbauen, sondern die in Ihrer Datenbank enthaltenen Daten anzeigen.

Das macht nur Sinn, wenn der anzuzeigende Ausschnitt der Daten hierarchisch aufgebaut ist. Die Daten können dabei aus einer einzelnen oder auch aus mehreren Tabellen stammen.

Wenn die Daten mehrerer Tabellen angezeigt werden sollen, müssen diese per 1:n-Beziehung miteinander verknüpft sein.

Auf diese Weise können Sie dann beispielsweise Kunden, Bestellungen und bestellte Artikel abbilden.

Bei einer einzelnen Tabelle kann es sich beispielsweise um eine Mitarbeitertabelle handeln, die etwa über ein Feld namens VorgesetzterID mit sich selbst verknüpft ist und die Hierarchie der Mitarbeiter abbildet.

In dem Fall zeigt das Treeview-Steuerelement nur die Mitarbeiter an. Die Verknüpfung einer Tabelle mit sich selbst heißt reflexive Verknüpfung.

Daten aus verknüpften Tabellen anzeigen

Beim Anzeigen von Daten aus verknüpften Tabellen werden die in den Tabellen enthaltenen Datensätze entsprechend der Abbildung im Treeview-Steuerelement durchlaufen.

Bei zwei Tabellen bedeutet dies, dass in einer äußeren Schleife die Datensätze der Haupttabelle und in einer inneren Schleife die Datensätze der untergeordneten Tabelle durchlaufen werden.

Beispiel: Kunden und Bestellungen

Am besten lässt sich das an einem Beispiel erklären. Dazu fügen Sie einem leeren Formular ein neues Treeview-Steuerelement hinzu und nennen es tvwTreeview.

Anschließend benötigen Sie nur noch eine Routine, die beim Öffnen des Formulars ausgelöst wird und die Kunden- und Bestelldaten in das Treeview-Steuerelement schreibt. Diese Routine sieht wie in Quellcode 1 aus.

Die Routine deklariert und instanziert ein Database- und zwei Recordset-Objekte zum Referenzieren der beiden Tabellen Kunden und Bestellungen.

In der ersten Do While-Schleife werden die Kunden durchlaufen. Dabei wird für jeden Kunden ein Knoten im Treeview angelegt. Innerhalb dieser Schleife wird außerdem das Recordset-Objekt rstBestellungen mit den Bestellungen gefüllt, die zum aktuellen Kunden gehören. Diese werden in einer weiteren Do While-Schleife unterhalb des aktuellen Kunden in das Treeview-Steuerelement eingefügt.

Werte der Key-Eigenschaft

Zu beachten ist dabei die Benennung der Key-Eigenschaft eines jeden Knotens. Dieser muss aus einer Zeichenkette mit einem Buchstaben am Anfang bestehen und eindeutig sein. Außerdem sollte man aus dieser Eigenschaft möglichst den zugrunde liegenden Datensatz identifizieren können, also den Primärschlüssel oder einen anderen eindeutigen Wert in den Key-Wert integrieren.

Normalerweise könnte man einfach einen beliebigen Buchstaben (etwa "a") verwenden und hinten den Wert des Primärschlüsselfeldes der Tabelle anhängen:

"a" & rst!ID

Wenn Sie es aber wie im vorliegenden Fall mit mehr als einer Tabelle zu tun haben, ist die Eindeutigkeit der Primärschlüsselfelder nicht mehr gewährleistet: Es kann ja durchaus in beiden Tabellen einen Primärschlüssel mit dem Wert 1 geben.

Also müssen Sie sich etwas anderes einfallen lassen. Die Lösung ist allerdings recht einfach: Da der Key-Wert eine beliebige Zeichenkette beinhalten darf, können Sie diesen einfach je Tabelle mit einem anderen Wert beginnen lassen. Im vorliegenden Fall beginnen die Key-Werte dementsprechend mit "Kunde" und "Bestellung".

Das Ergebnis der Routine sieht wie in Abb. 1 aus. Per Doppelklick auf einen der Kunden zeigen Sie die zu dem Kunden gehörenden Bestellungen an.

Abb. 1: Einfache hierarchische Daten im Treeview-Steuerelement

Reflexive Daten im Treeview-Steuerelement anzeigen

Die Tabelle Personal der Nordwind-Datenbank liefert ein gutes Beispiel für eine reflexive Beziehung. Einige der Datensätze sind über das Feld Vorgesetzte(r) mit Feldern der gleichen Tabelle verknüpft und liefern damit Informationen über die Hierarchie der Mitarbeiter.

Diese lässt sich natürlich in einem Treeview-Steuerelement weitaus besser veranschaulichen als in einer Tabelle (s. Abb. 2).

Quellcode 1: Treeview-Steuerelement mit Daten aus zwei verknüpften Tabellen füllen

Dim objTreeview As MSComctlLib.Treeview

Private Sub Form_Open(Cancel As Integer)

    Dim db As DAO.Database

    Dim rstKunden As DAO.Recordset

    Dim rstBestellungen As DAO.Recordset

    Dim objNode As MSComctlLib.Node

    Set db = CurrentDb

    Set rstKunden = db.OpenRecordset("Kunden")

    Set objTreeview = Me.ctlTreeview.Object

    Do While Not rstKunden.EOF

        Set objNode = objTreeview.Nodes.Add(, , "Kunde" & rstKunden![Kunden-Code], _
            rstKunden!Firma)

        Set rstBestellungen = db.OpenRecordset _
            ("SELECT * FROM Bestellungen WHERE [Kunden-Code] = '" & rstKunden![Kunden-Code] & "'")

        Do While Not rstBestellungen.EOF

            objTreeview.Nodes.Add "Kunde" & rstKunden![Kunden-Code], tvwChild, "Bestellung" _
                & rstBestellungen![Bestell-Nr], rstBestellungen![Bestell-Nr] & "/" _
                & rstBestellungen!Bestelldatum

            rstBestellungen.MoveNext

        Loop

        rstKunden.MoveNext

    Loop

    Set objNode = Nothing

    rst.Close

    Set rst = Nothing

    Set db = Nothing

End Sub

Abb. 2: Mitarbeiter und ihre Untergebenen

Für das Füllen des Treeview-Steuerelements mit Daten aus einer reflexiven Beziehung ist im Prinzip Rekursion erforderlich, das heißt, dass eine Funktion sich entsprechend der Hierarchie immer wieder selbst aufruft und dabei untergeordnete Objekte abarbeitet, bis schließlich alle Daten berücksichtigt wurden.

Quellcode 2 zeigt die für diesen Fall notwendigen Routinen. Die Beim Laden-Ereignisprozedur weist der Objektvariablen objTreeview das Treeview-Steuerelement des Formulars zu und ruft das erste Mal die Routine TreeviewRekursivFuellen auf. Der einzige Parameter der Routine erhält hier den Wert 0.

Beim ersten Aufruf erzeugt die Routine TreeviewRekursivFuellen eine Datensatzgruppe, die alle Mitarbeiter ohne Vorgesetzten enthält. Dafür sorgt der Parameter lngPersonalNr; hat dieser den Wert 0, wird die entsprechende SELECT-Abfrage ausgeführt.

Die Routine legt dann zunächst einen Knoten für den ersten Mitarbeiter an (für lngPersonalNr = 0 ohne Referenz auf bestehende Knoten) und ruft sich selbst dann mit einem anderen Wert für den Parameter lngPersonalNr auf - nämlich der Personalnummer des soeben angelegten Mitarbeiters.

Quellcode 2: Füllen eines Treeview-Steuerelements mit einer rekursiven Funktion

Dim objTreeview As MSComctlLib.Treeview

Private Sub Form_Load()

    Set objTreeview = Me.ctlTreeview.Object

    TreeviewRekursivFuellen 0

End Sub

Private Sub TreeviewRekursivFuellen(lngPersonalNr As Long)

    Dim db As DAO.Database

    Dim rst As DAO.Recordset

    Dim objNode As MSComctlLib.Node

    Set db = CurrentDb

    If lngPersonalNr = 0 Then

        Set rst = db.OpenRecordset("SELECT * FROM Personal WHERE [Vorgesetzte(r)] IS NULL")

    Else

        Set rst = db.OpenRecordset("SELECT * FROM Personal WHERE [Vorgesetzte(r)] = " _
            & lngPersonalNr)

    End If

    Do While Not rst.EOF

        If lngPersonalNr = 0 Then

            Set objNode = objTreeview.Nodes.Add(, , "Personal" & rst![Personal-Nr], _
                rst!Vorname & " " & rst!Nachname)

        Else

            Set objNode = objTreeview.Nodes.Add("Personal" & lngPersonalNr, tvwChild, _
                "Personal" & rst![Personal-Nr], rst!Vorname & " " & rst!Nachname)

        End If

        TreeviewRekursivFuellen rst![Personal-Nr]

        rst.MoveNext

    Loop

    Set objNode = Nothing

    rst.Close

    Set rst = Nothing

    Set db = Nothing

End Sub

Diesmal prüft die Routine, ob es Mitarbeiter gibt, die dem Mitarbeiter mit der übergebenen Personalnummer untergeordnet sind.

Ist das der Fall, werden diese ebenfalls angelegt. Dabei wird allerdings eine Referenz auf den übergeordneten Mitarbeiter-Knoten erzeugt.

Anschließend wird die Routine erneut mit der aktuellen Personalnummer aufgerufen - dieser Vorgang wiederholt sich, bis es keine weiteren untergeordneten Mitarbeiter mehr gibt.

In diesem Fall wird die aufrufende Instanz der Routine TreeviewRekursivFuellen weiter abgearbeitet.

Performance-Tuning

Das Füllen von Treeview-Steuerelementen kann je nach der Menge der anzuzeigenden Daten eine Weile dauern.

Es gibt verschiedene Möglichkeiten, den Aufbau des Baumes zu beschleunigen. Bei bekannter Verschachtelungstiefe können Sie etwa folgende Variante verwenden: Die Anzeige von Kunden und Bestellungen aus dem weiter oben beschriebenen Beispiel erzeugt beispielsweise für jeden Kunden ein neues Recordset-Objekt mit den passenden Bestellungen.

Dies können Sie sich sparen, indem Sie direkt alle benötigten Informationen in einer einzigen Abfrage zusammenfassen. Diese enthält im vorliegenden Fall die beiden Tabellen Kunden und Bestellungen und die in Abb. 3 abgebildeten Felder, wobei diese nach den Primärschlüsselfeldern der beiden Tabellen sortiert sind.

Quellcode 3: Schnellere Variante zum Füllen eines Treeviews mit Kunden und Bestellungen

Private Sub Form_Load()

    Dim db As DAO.Database

    Dim rst As DAO.Recordset

    Dim objNode As MSComctlLib.Node

    Dim strVorherigerKunde As String

    Set db = CurrentDb

    Set rst = db.OpenRecordset("qryKundenBestellungen")

    Set objTreeview = Me.ctlTreeview.Object

    Do While Not rst.EOF

        If Not rst![Kunden-Code] = strVorherigerKunde Then

            Set objNode = objTreeview.Nodes.Add(, , "Kunde" & rst![Kunden-Code], rst!Firma)

        End If

        Set objNode = objTreeview.Nodes.Add("Kunde" & rst![Kunden-Code], tvwChild, "Bestellung" _
            & rst![Bestell-Nr], rst![Bestell-Nr] & "/" & rst!Bestelldatum)

        strVorherigerKunde = rst![Kunden-Code]

        rst.MoveNext

    Loop

    Set objNode = Nothing

    rst.Close

    Set rst = Nothing

    Set db = Nothing

End Sub

Die Routine aus Quellcode 3 durchläuft alle enthaltenen Datensätze. Dabei speichert sie jeweils den Kunden-Code des aktuellen Kunden in der Variablen strVorherigerKunde zwischen. Für das Folgende ist ein Blick auf Abb. 4 hilfreich.

Im ersten Durchlauf ist strVorherigerKunde noch leer, also wird der Kunden-Knoten angelegt. Im gleichen Zuge wird der erste Bestellungs-Knoten angelegt - alles mit den Daten des ersten Datensatzes.

Beim zweiten Datensatz stellt die Routine fest, dass der Kunden-Code dem Wert von strVorherigerKunde entspricht, und legt keinen neuen Knoten für diesen Knoten, sondern nur die im zweiten Datensatz enthaltene Bestellung an.

Die beiden Beispielformulare frmKundenBestellungen und frmKundenBestellungenSchnell geben beim Anzeigen die für den Aufbau des Baumes benötigte Zeit im Direktfenster aus - werfen Sie einmal einen Blick darauf. Bei Verwendung der in der Beispieldatenbank enthaltenen Daten benötigt die zuletzt vorgestellte Variante nur die Hälfte der Zeit der langsameren Version.

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.