“Ein TreeView füllen – mit hierarchischen Daten Das ist doch wohl ein alter Hut!” Das stimmt wohl, denn genau dafür ist dieses Steuerelement ja nun auch gemacht. Wir vereinfachen das Füllen des TreeViews aber ein wenig, indem wir eine Methode bereitstellen, bei der Sie nur noch die Namen der betroffenen Tabelle in eine weitere Tabelle schreiben und die beim Klicken auf die Elemente des TreeViews durchzuführenden Aktionen festlegen müssen.
Haben Sie schon mal ein TreeView programmiert, das zumindest die üblichen Funktionen wie das Hinzufügen untergeordneter Elemente, das Löschen des aktuellen Knotens, das Auf- und Zuklappen von Zweigen, sowie das automatische Speichern des Zustands des TreeViews implementiert
Und welches zusätzlich, um die Performance zu verbessern, nur jeweils die sichtbaren Elemente plus der folgenden Ebene anlegt, damit nicht alle Elemente gleich zu Beginn geladen werden müssen und dies somit möglicherweise zu Verzögerungen führt
Nun, dann wissen Sie, wie viel Arbeit so etwas verursachen kann – egal, ob Sie die Daten aus den Tabellen eines hierarchisch aufgebauten Datenmodells beziehen oder aus einer Tabelle, die reflexiv mit sich selbst verknüpft ist.
Wenn Sie sich mit ein paar Randbedingungen abfinden und die nachfolgend vorgestellten Standardfunktionen für Ihren Anwendungsfall ausreichen, können Sie die Lösung dieses Beitrags gleich einsetzen. Wenn nicht, müssen Sie entweder selbst noch Funktionen hinzufügen oder uns die fehlenden Funktionen mitteilen – wir werden uns nicht scheuen, sinnvolle Erweiterungen für Sie zu entwickeln und in einer späteren Ausgabe von Access im Unternehmen zu veröffentlichen.
Bild 1 zeigt ein Beispiel für ein mit dem hier vorgestellten Tool gesteuerten TreeView-Steuerelement. Sie können damit sowohl die Daten aus hierarchisch angeordneten Tabellen als auch von Daten aus reflexiven Beziehungen darstellen – auch gemischt. In den folgenden Abschnitten erfahren Sie, wie dies funktioniert und welche Voraussetzungen nötig sind.
Bild 1: TreeView mit hierarchischen und reflexiven Tabellen
Voraussetzungen des Datenmodells
Die Voraussetzungen beziehen sich in erster Linie auf den Aufbau der Tabellen, deren Daten im TreeView angezeigt werden sollen. Diese sehen so aus:
- Jede Tabelle muss einen aus einem einzigen Feld bestehenden Primärschlüssel mit dem Datentyp Long besitzen.
- Jede Tabelle, die einer Tabelle untergeordnet ist, muss ein Fremdschlüsselfeld zur Herstellung der Verknüpfung mit der übergeordneten Tabelle besitzen (nur die Tabelle mit den Elementen der obersten Ebene braucht kein Fremdschlüsselfeld).
- Wenn eine Tabelle mit sich selbst verknüpft ist, muss sie ein zusätzliches Fremdschlüsselfeld besitzen, das den Wert des Primärschlüsselfelds des übergeordneten Datensatzes enthält.
- Wenn eine Tabelle mit sich selbst verknüpft ist und das Root-Element, also das oberste Element, mit einem Element aus einer anderen Tabelle verknüpft werden soll, muss es natürlich noch ein weiteres Fremdschlüsselfeld zur Verknüpfung mit dieser anderen Tabelle aufweisen – neben dem Fremdschlüsselfeld für die Herstellung der reflexiven Beziehung. Es darf dann nur eines der beiden Fremdschlüsselfelder gefüllt sein.
- Jede Tabelle muss ein Feld mit dem anzuzeigenden Text enthalten.
- Jede Tabelle muss ein Ja/Nein-Feld zum Speichern der Expanded-Eigenschaft des entsprechenden Elements besitzen. Dieses legt fest, ob eventuell untergeordnete Elemente direkt beim Öffnen des TreeViews ausgeklappt werden.
- Jede Tabelle muss ein Feld zum Speichern des Namens einer Bilddatei enthalten, das in Zusammenhang mit dem jeweiligen Element angezeigt werden soll. Auch wenn dieses Feld nicht mit Daten gefüllt werden muss, so sollte es doch vorhanden sein. Die Bilddateien werden in einer speziellen Tabelle gespeichert – dazu später mehr.
Abfragen statt Tabellen
Die geforderten Informationen können auch in Form einer Abfrage bereitgestellt werden. Dies macht beispielsweise Sinn, wenn Personennamen im TreeView angezeigt werden sollen und diese Namen nicht in einem einzelnen Feld, sondern in Feldern wie Vorname und Nachname liegen. Sie können dann eine Abfrage definieren, die diese Felder zu einem Feld zusammenfasst, das dann als Herkunft für die anzuzeigenden Felder dient.
Auch kann es sein, dass eine reflexive Beziehung nicht direkt über ein in der Tabelle befindliches Fremdschlüsselfeld hergestellt wird, sondern über eine Verknüpfungstabelle, welche neben einem Primärschlüsselfeld zwei Fremdschlüsselfelder für die Aufnahme der beiden Primärschlüsselwerte der beteiligten Datensätze aufnehmen soll. Auch hier müssen Sie eine Abfrage so erstellen, dass diese die eigentlichen Daten der Tabelle enthält und auch die Verknüpfungstabelle aufnimmt. Ein Beispiel finden Sie weiter unten.
Notwendige Elemente
Die hier vorgestellte Lösung ist kein externes Tool, das Ihnen den Code für das TreeView-Steuerelement zusammenbaut, sondern es liefert selbst den Code. Es erstellt also kein TreeView, sondern es steuert seine Erstellung und auch das TreeView und seine Elemente selbst. Aus der Beispieldatenbank zu diesem Beitrag müssen Sie die folgenden Elemente in die Zieldatenbank importieren:
- Tabelle tblContextMenuItems
- Tabelle tblImages
- Tabelle tblTreeViewConf
- Tabelle tblTreeViews
- Abfrage qryImages
- Formular frmTreeViewConf
- Formular sfmContextmenuItems
- Formular sfmTreeViewConf
- Modul mdlDatabase
- Modul mdlErrorHandlung
- Modul OGL2007
- Modul mdlOLE
- Klassenmodul clsCommandBarButton
- Klassenmodul clsCommandBarButtonCollection
- Klassenmodul clsTreeView
Notwendige Verweise
Außerdem brauchen Sie einen Verweis auf die Microsoft Office x.0 Object Library sowie OLE Automation, die Sie im Verweise-Dialog (Extras|Verweise) des VBA-Editors einstellen.
Grundgerüst
Das Grundgerüst unserer Lösung besteht aus einem Formular mit den folgenden Steuerelementen:
- Microsoft TreeView Control 6.0
- Microsoft ImageList Control 6.0
Hinzu kommt, soweit gewünscht, ein Unterformularsteuerelement zur Anzeige der Unterformulare mit den Detaildaten zum jeweils im TreeView-Steuerelement ausgewählten Element. Wenn Sie die oben genannten Steuerelemente zu einem neuen Formular namens frmProjekte hinzugefügt haben, können Sie das Projekt erstmalig kompilieren, was im Erfolgsfall heißt, dass alle Komponenten und Verweise vorliegen.
Steuerelemente benennen
Da das ImageList-Steuerelement und das TreeView-Steuerelement später vom Code aus referenziert werden sollen, vergeben Sie nun aussagekräftige Namen für diese Elemente, am besten die folgenden:
- TreeView-Steuerelement: ctlTreeView
- ImageList-Steuerelement: ctlImageList
TreeView konfigurieren
Wir kümmern uns zunächst nur um das Anlegen der nötigen Tabellen und um das Füllen des TreeView-Steuerelements. Zuallererst benötigen wir eine Tabelle, welche die Daten für die Elemente der ersten Ebene liefert.
Diese Tabelle nennen wir tblProjekte und füllen sie mit den Feldern aus Bild 2. Dies sind die mindestens notwendigen Felder für die Integration der Daten einer Tabelle in das TreeView – zumindest unter Verwendung unserer Lösung. Legen Sie dann zu Beispielzwecken einige Datensätze wie in Bild 3 an.
Bild 2: Die Tabelle mit den Elementen der obersten Ebene in der Entwurfsansicht …
Bild 3: … und in der Datenblattansicht mit einigen Einträgen.
TreeViewHandler aufrufen
Damit das Formular beim Öffnen mit den gewünschten Daten gefüllt wird, müssen Sie dafür sorgen, dass es eine spezielle Klasse namens clsTreeViewHandler instanziert und dieser einige Informationen zuweist. Dies ist nicht besonders aufwendig und kann in der Ereignisprozedur Form_Load geschehen. Theoretisch ginge auch Form_Open, hier kann es jedoch zu Problemen kommen, weil die ActiveX-Steuerelemente möglicherweise noch nicht initialisiert sind.
Für den Verweis auf die Klasse clsTreeViewHandler legen Sie folgende modulweit gültige Variable im Klassenmodul des Formulars fest:
Private WithEvents objTreeViewHandler As µ clsTreeViewHandler
Diese instanzieren Sie gleich in der ersten Zeile der Ereignisprozedur Form_Load. Anschließend weisen Sie dem neu erzeugten Objekt Verweise auf das TreeView-Steuerelement und das ImageList-Steuerelement zu und rufen die beiden Methoden InitTreeView und FillTree auf:
Private Sub Form_Load() Set objTreeViewHandler = New clsTreeViewHandler With objTreeViewHandler Set .TreeViewInst = Me.ctlTreeView.Object Set .ImageListInst = Me.ctlImageList.Object .InitTreeView (1) .FillTree End With End Sub
InitTreeView erwartet noch einen Parameter, zu diesem kommen wir gleich. Erstmal müssen wir noch festlegen, welche Daten überhaupt im TreeView angezeigt werden sollen – wir sind noch nicht so weit, dass sich der TreeViewHandler die benötigten Tabellen selbst zusammensucht.
Dazu öffnen Sie das Formular frmTreeViewConf, das erst einmal eine InputBox zur Eingabe des Namens eines TreeViews voranschickt (siehe Bild 4). Die Eingabe speichert der TreeView-Konfigurator in der Tabelle tblTreeViews. Der Hintergrund ist, dass eine Datenbank durchaus mehr als ein TreeView-Steuerelement enthalten kann (genau genommen kann sogar ein einziges Formular mehr als ein TreeView-Steuerelement anzeigen) und der TreeView-Konfigurator diese auch verwalten können soll.
Bild 4: Abfrage der Bezeichnung des TreeViews
Dann folgt der große Moment: Der TreeView-Konfigurator erscheint und erschlägt Sie vermutlich erstmal durch seine vielen Elemente (siehe Bild 5). Das TreeView-Steuerelement Projektbaum ist im oberen Kombinationsfeld bereits ausgewählt.
Bild 5: Der TreeView-Konfigurator
Der obere Teil zeigt eine Übersicht aller im TreeView anzuzeigenden Tabellen an. Dort wählen Sie eine Tabelle aus und erhalten im Bereich darunter alle Details dieser Tabelle, die Sie dort auch direkt bearbeiten können. Außerdem finden Sie dort Steuerelemente zum Anlegen einer neuen Tabelle, zum Löschen der aktuell angezeigten Tabelle, sowie zum Speichern.
Der untere Bereich zeigt ebenfalls eine Liste an, die Sie allerdings direkt bearbeiten können. Dort tragen Sie alle Kontextmenüeinträge ein, die in Zusammenhang mit den Elementen der aktuellen Tabelle im TreeView erscheinen sollen, wenn der Benutzer mit der rechten Maustaste darauf klickt. Wie Sie Code anlegen, um auf diese Mausklicks zu reagieren, erfahren Sie später.
Nun tragen Sie die bereits angelegte Tabelle tblProjekte in den TreeView-Konfigurator ein. Die Daten dieser Tabelle sehen nun etwa so wie in Bild 6 aus. Im Einzelnen bedeuten diese Einträge Folgendes:
Bild 6: Die Werte für die erste Tabelle im TreeView-Konfigurator
- ThisTable: Name der Tabelle, welche die Daten für die Elemente dieser Ebene liefert
- KeyChar: Jedes TreeView-Element erhält als eindeutige Kennung einen Key, der aus einem Buchstaben und dem Wert des Primärschlüsselfelds des entsprechenden Datensatzes besteht. Der hier festgelegte KeyChar muss innerhalb des TreeViews eindeutig sein, da über ihn die Tabelle identifiziert wird, aus der ein Element stammt.
- PKIDField: Name des Primärschlüsselfeldes der Tabelle dieser Ebene
- NodetextField: Name des Feldes, das den im TreeView-Element anzuzeigenden Text enthält
- NodeImageField: Name des Feldes, das die Bezeichnung des Bilds aus dem ImageList-Steuerelement enthält, das für die Elemente dieser Tabelle standardmäßig angezeigt werden soll
- NodeExpandedField: Name des Ja/Nein-Feldes, das den Expanded/Collapsed-Zustand des vom Element des aktuellen Datensatzes ausgehenden Zweigs festlegt
Der erste Test
Vor dem ersten Test ist noch eine Anpassung im Code nötig: Wir haben der Methode InitTreeView in der Ereignisprozedur Form_Load zunächst den Wert 1 als Parameter mitgegeben.