TreeView-Konfigurator

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

“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.

pic001.png

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.

pic002.png

Bild 2: Die Tabelle mit den Elementen der obersten Ebene in der Entwurfsansicht …

pic003.png

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.

pic004.png

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.

pic005.png

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:

pic006.png

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.

Dort muss aber die ID der soeben im TreeView-Konfigurator angelegten TreeViews-Konfiguration stehen, die Sie dem Textfeld aus Bild 7 entnehmen können.

pic017.png

Bild 7: Der TreeView-Konfigurator zeigt die ID des aktuell ausgewählten TreeViews an.

Das Ergebnis ist ernüchternd: Die beiden Einträge der ersten angelegten Tabelle sehen ein wenig aus wie bestellt und nicht abgeholt – dies bekommt man fast auch mit einem einfachen Listenfeld hin (siehe Bild 8).

pic007.png

Bild 8: Das TreeView-Steuerelement mit der ersten Beispieltabelle

Erstmal fehlt hier ein schickes Icon und zweitens wären Plus-Zeichen zum Aufklappen untergeordneter Elemente nicht schlecht (und entsprechende Minus-Zeichen zum Minimieren).

Okay: Um die Icons haben wir uns noch gar nicht gekümmert und um Plus-Zeichen zum Ein- und Ausklappen von untergeordneten Elementen braucht man erstens erst einmal untergeordnete Elemente.

Zweite Tabelle hinzufügen

Fügen wir also eine weitere Tabelle zum TreeView-Konfigurator hinzu. Diese soll tblMeilensteine heißen und enthält gegenüber der Tabelle tblProjekte noch ein weiteres Feld zum Herstellen der Beziehung mit dieser Tabelle. Im Beziehungsfenster der Datenbank sieht dies wie in Bild 9 aus. Die Tabelle tragen Sie nun ebenfalls in den TreeView-Konfigurator ein, und zwar mit den folgenden Werten:

pic008.png

Bild 9: Die Tabelle tblMeilensteine und ihre Verknüpfung mit der Tabelle tblProjekte

  • ThisTable: tblMeilensteine
  • ParentTable: tblProjekte
  • KeyChar: m
  • PKIDField: ID
  • FKIDField: ProjektID
  • NodetextField: Meilenstein
  • NodeImageField: Image
  • NodeExpandedField: Expanded

Das Ergebnis kann sich schon eher sehen lassen (siehe Bild 10). Es erscheinen zumindest einige untergeordnete Elemente (die Sie allerdings zuvor noch anlegen müssen).

pic009.png

Bild 10: Schon besser: Unterelemente zum Ein- und Ausklappen

Sonderfälle für untergeordnete Tabellen

Auch das System für das Hinzufügen weiterer Tabellen sollte nun klar sein. Es gibt eigentlich nur noch zwei Sonderfälle, die wichtig sind:

  • Sie können auch zwei oder mehr Tabellen unter einer bestehenden Tabelle anordnen. Sie müssen dazu lediglich den gleichen Tabellennamen unter ParentTable eintragen.
  • Der TreeView-Konfigurator verarbeitet auch Tabellen mit einer reflexiven Beziehung – mehr dazu in den folgenden Abschnitten.

Behandlung reflexiver Beziehungen

Wenn eine Tabelle mit sich selbst verknüpft ist, tragen Sie diese einfach zweimal in den TreeView-Konfigurator ein. Der erste Eintrag dient dazu, die Einträge ganz normal unterhalb der Elemente einer übergeordneten Tabelle einzusortieren. Der zweite stellt dann die Beziehung der Elemente dieser Tabelle untereinander her.

Im Gegensatz zur üblichen Notation von Tabellen im TreeView-Konfigurator tragen Sie als ParentTable die gleiche Tabelle nochmal ein, die Sie bereits als ThisTable eingetragen haben, und als FKIDField geben Sie das Fremdschlüsselfeld an, das den Wert des Primärschlüsselfeldes des Datensatzes in der gleichen Tabelle enthält, dem der aktuelle Datensatz untergeordnet werden soll.

Das Ergebnis sollte etwa so aussehen wie in Bild 11. Dort wurden einige Aufgaben-Elemente untereinander angeordnet. Die Tabelle tblAufgaben muss dazu freilich ein weiteres Fremdschlüsselfeld erhalten, sodass diese nun wie in Bild 12 aussieht.

pic011.png

Bild 11: TreeView mit reflexiv verknüpften Elementen

pic010.png

Bild 12: Die Tabelle tblAufgaben besitzt mit KundenstoryID und ParentaufgabeID zwei Fremdschlüsselfelder.

Was fürs Auge

Weiter oben haben wir das Fehlen von Icons bemängelt. Auch hier nimmt der TreeView-Konfigurator Ihnen einige Arbeit ab. Im harten Entwickleralltag müssten Sie zunächst alle benötigten Bilder in das dafür vorgesehene ImageList-Steuerelement laden und den TreeView-Elementen dann beim Anlegen die Bezeichnung der Bilder mit auf den Weg geben. Der Weg des TreeView-Konfigurators sieht ganz anders aus:

  • Die Bilddateien werden im OLE-Feld Image der Tabelle tblImages gespeichert, und zwar im Originalformat der Bilddatei. Das zweite Feld dieser Tabelle soll den Dateinamen enthalten, also beispielsweise application.png.
  • Die Klasse clsTreeViewHandler sorgt beim Initialisieren dafür, dass alle Bilder aus der Tabelle ausgelesen und in das ImageList-Steuerelement geschrieben werden.

Dabei gibt es eine Besonderheit: Normalerweise sollten alle Elemente einer Tabelle auch das gleiche Bild anzeigen. Möglicherweise wollen Sie aber über das Bild verschiedene Status verschiedener Elemente aus der gleichen Tabelle darstellen – beispielsweise um erledigte Aufgaben mit einem Häkchen zu versehen.

Daher enthält auch jede Tabelle ein Feld namens Image, das für jeden Datensatz eine individuelle Bildbezeichnung aufnimmt. Beim Füllen des TreeView-Steuerelements prüft der TreeView-Konfigurator dann, ob der einzelne Datensatz ein individuelles Bild erfordert, und zeigt dieses gegebenenfalls an.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar