Treeview ohne MSCOMCTL

Im Juli/August 2017 war es wieder mal soweit: Microsoft hat ein Update für einige Office-Varianten geliefert, das die TreeView-Steuerelemente in den Anwendungen auf den betroffenen Rechnern lahmgelegt hat. Zwar gibt es ein paar Wochen später immer einen Patch oder ein weiteres Update zur Behebung des Fehlers, aber wer kurzfristig handeln muss, darf manuell an der Datei MSCOMCTL.ocx und/oder der Registry herumpfuschen. Das kann man natürlich keinem Kunden zumuten, daher zeigen wir eine mögliche Lösung.

Normalerweise beschreiben wir in Access im Unternehmen keine Tools von Drittanbietern, die kostenpflichtig sind. Im Falle des Treeviews scheint es jedoch sinnvoll, eine Ausnahme zu machen. Thomas Pfoch hat mit seiner Firma picoware einen TreeView-Ersatz programmiert, der komplett ohne zusätzliche Dateien wie .ocx– oder .dll-Dateien auskommt.

Er verwendet stattdessen ein Unterformular, das er geschickt aufbohrt, um Daten nach den Vorgaben des Benutzers anzuzeigen und diese – wie es für ein Treeview typisch ist – für die untergeordneten Ebenen nach rechts einzurücken.

Wenn Sie diese Erweiterung nutzen, müssen Sie lediglich einige Objekte aus der Beispieldatenbank, die Sie nach dem Erwerb des Pakets erhalten, in die Zieldatenbank kopieren. Darunter befindet sich auch ein Formular, das Sie als Unterformular in das Zielformular einfügen.

Im Code, der beim Laden des Zielformulars ausgelöst wird, tragen Sie dann die Anweisungen ein, welche die anzuzeigenden Daten definieren.

Der picoware-Treeview kostet 299,- EUR (inkl. 19% MwSt.) pro Entwicklerlizenz. Das heißt, dass Sie für jeden Entwickler, der das Tools einsetzt, eine Lizenz benötigen. Dieser Entwickler kann allerdings beliebig viele Anwendungen mit den Treeview-Funktionen ausstatten und weitergeben. Gemessen an dem ärger, den die regelmäßigen Updates der Datei MSCOMCTL.ocx mit sich bringen, ist dies nach unserem Ermessen eine sinnvolle Investition.

Hinzu kommt, dass Sie als Leser von Access im Unternehmen im Shop unter www.amvshop.de 20% Rabatt erhalten, und zwar mit dem Gutscheincode aiu-treeview.

Vorteile des picoware-Treeviews

Zu den Vorteilen des picoware-Treeviews gehören die folgenden:

  • Es ist keine externe Komponente mehr erforderlich, die durch Updates kompromittiert werden könnte.
  • Es funktioniert nicht nur unter 32bit-Versionen von Access, sondern auch unter 64bit.
  • Die Elemente können per spezieller SQL-Abfrage und somit mit wesentlich weniger Code zugewiesen werden.

Nachteile des picoware-Treeviews

Durch die Anpassung an die Eigenarten einer Datenbank mit ihren in Tabellen gespeicherten Daten und durch die Verwendung eines Unterformulars zur Realisierung TreeViews ergeben sich auch ein paar Nachteile:

  • Bestehende MSCOMCTL-TreeViews lassen sich nicht ohne weiteres in das picoware-Treeview umwandeln.
  • Durch die Verwendung eines Unterformulars kommt es beim Aktualisieren von Daten und anderen Vorgängen manchmal zum Neuaufbau des Treeviews.

Beispieldatenbank

Wenn Sie nicht sicher sind, ob das Tool das Richtige für Sie ist, können Sie unter dem Link http://shop.minhorst.com/access-tools/322/picoware-treeview Beispieldatenbanken für verschiedene Access-Versionen herunterladen, welche die Möglichkeiten des Tools demonstrieren. Auf den folgenden Seiten zeigen wir Ihnen, wie Sie das picoware-Treeview programmieren.

picoware-Treeview-Elemente hinzufügen

Nach dem Erwerb und dem Download erhalten Sie eine Datenbank etwa namens treeview_20170629_1451_Source.mdb. Diese enthält alle Objekte, die Sie für den Einsatz in eigenen Datenbanken benötigen.

Sie brauchen sich keine Mühe zu machen, die benötigen Objekte selbst in die Zieldatenbank zu ziehen. Die Datenbank aus dem Download enthält ein Tool, das Ihnen diese Arbeit abnimmt. Dazu öffnen Sie das Formular namens Tool_CopyCode. Dieses zeigt eine Treeview-Ansicht der Verzeichnis- und Dateistruktur auf Ihrer Festplatte an, wobei das Verzeichnis der Quelldatenbank geöffnet wird (s. Bild 1).

Auswahl der Zieldatenbank für die Datenbankobjekte

Bild 1: Auswahl der Zieldatenbank für die Datenbankobjekte

Wählen Sie hier die Zieldatenbank aus und klicken Sie auf die Schaltfläche Copy. Dies kopiert nun alle notwendigen Datenbankobjekte in die ausgewählte Datenbank. Falls nötig, halten Sie dabei die Umschalttaste gedrückt.

Erstes Beispiel: Kategorien auflisten

Im ersten Beispiel wollen wir einfach die Daten der Tabelle tblKategorien im Treeview anzeigen. Dazu erstellen Sie ein neues Formular namens frmKategorien, öffnen es in der Entwurfsansicht und ziehen das Formular USys_pTV_TreeView als Unterformular in den Entwurf. Stellen Sie den Namen des Unterformular-Steuerelements auf sfmTreeView ein.

Passen Sie außerdem die Größe des Unterformulars an, das initial in der vollen Breite eingefügt wird (s. Bild 2). Stellen Sie außerdem die Eigenschaften Bildlaufleisten, Datensatzmarkierer, Navigationsschaltflächen und Trennlinien auf den Wert Nein ein – das Hauptformular selbst zeigt keine Daten an, sodass wir diese Elemente nicht benötigen.

Einfügen des Unterformulars und Anpassen der Größe

Bild 2: Einfügen des Unterformulars und Anpassen der Größe

Ein erster Wechsel in die Formularansicht liefert natürlich noch kein sinnvolles Ergebnis – kein Wunder, denn wir füllen das Treeview ja auch noch nicht mit Daten (s. Bild 3). Das ändern wir allerdings in den folgenden Schritten.

Erster Wechsel in die Formularansicht

Bild 3: Erster Wechsel in die Formularansicht

Dazu fügen Sie dem Klassenmodul des Formulars frmKategorien, das Sie durch Einstellen der Eigenschaft Enthält Modul des Formulars anlegen, eine Objektvariable hinzu, welche das Unterformular zur Anzeige des Treeviews referenzieren soll:

Public WithEvents objTreeView As  Form_USys_pTV_TreeView

Die Deklaration enthält das Schlüsselwort With-Events, damit wir innerhalb der Klasse auch die Ereignisse dieses Formulars beziehungsweise der Formularklasse Form_USys_pTV_TreeView implementieren können.

Damit wir das Unterformular zur Anzeige des Treeviews füllen können, weisen wir der Objektvariablen objTreeView das im Unterformularsteuerelement sfmTreeView gespeicherte Formular zu, und zwar in der Prozedur, die durch das Ereignis Beim öffnen des Hauptformulars ausgelöst wird:

Private Sub Form_Open(Cancel As Integer)
     Set objTreeView = Me!Treeview.Form
End Sub

Treeview mit Daten füllen

Damit wäre das Treeview-Unterformular schon einmal referenziert. Nun wollen wir diese noch mit den gewünschten Daten füllen, in diesem Falle den Namen der Kategorien aus der Tabelle tblKategorien.

Beim MSCOMCTL.ocx-TreeView hätten Sie jedes Element einzeln mit der Add-Methode hinzufügen müssen. Hier spielt das speziell auf die Anzeige von Daten aus Tabellen oder Abfragen ausgelegte picoware-Treeview seine Stärken aus. Sie müssen lediglich eine SQL-Anweisung mit einem bestimmten Format definieren, welche die anzuzeigenden Daten liefert, und diese mit der AddSQL-Methode dem in der Variablen objTreeView gespeicherten Treeview zuweisen. Das sieht dann beispielsweise wie in Listing 1 aus.

Private Sub Form_Open(Cancel As Integer)
     Dim strSQL As String
     Set objTreeView = Me!Treeview.Form
     strSQL = "SELECT KategorieID AS Reference, Kategoriename AS Caption, ''''Kategorie'''' AS RefContext " _
         & "FROM tblKategorien"
     objTreeView.AddSQL strSQL
End Sub

Listing 1: Anzeigen der Kategorien beim öffnen des Formulars

Ein Wechsel zur Formularansicht liefert bereits ein akzeptables Ergebnis (s. Bild 4) – und das mit einer sehr überschaubaren Anzahl von Codezeilen!

Die Kategorien im picoware-Treeview

Bild 4: Die Kategorien im picoware-Treeview

Erläuterung des SQL-Ausdrucks

Der zum Füllen des Treeviews verwendete SQL-Ausdruck sieht zusammengefasst wie folgt aus:

SELECT KategorieID AS Reference, Kategoriename AS Caption, ''''Kategorie'''' AS RefContext FROM tblKategorien

Hier erkennen Sie drei Felder, die jeweils mit einem ALIAS-Namen ausgestattet wurden. Dies dient dem Zweck, dass das Treeview beim Anzeigen der Daten genau weiß, welches Feld die Daten für welchen Zwecke enthält. Wir verwenden die folgenden drei ALIAS-Bezeichnungen:

  • Reference: Das hier genannte Feld, in diesem Fall KategorieID, wird nicht angezeigt, sondern als Referenz verwendet, wenn die untergeordnete Ebene Daten aufnehmen soll, die über ein Fremdschlüsselfeld, das ebenfalls speziell gekennzeichnet wird, entsprechend verknüpfte Daten enthält.
  • Caption: Das mit dem ALIAS namens Caption versehene Feld enthält den im Treeview anzuzeigenden Wert, in diesem Fall den Inhalt des Feldes Kategoriename.
  • RefContext: Dieses Feld nimmt eine Zeichenkette auf, mit der Sie den Typ des daraus generierten Elements im TreeView definieren – in diesem Fall Kategorie. Im folgenden Beispiel werden Sie sehen, wie Sie die hierfür angegebene Zeichenkette nutzen können.

Artikel zu Kategorien hinzufügen

Nun gehen wir einen Schritt weiter und fügen in einer zweiten Ebene noch die zu einer jeden Kategorie gehörenden Artikel hinzu. Dazu kopieren Sie das soeben erstellte Formular und fügen es unter dem Namen frmKategorienArtikel erneut in die Datenbank ein (frmKategorie markieren, Strg + C, Strg + V, neuen Namen eingeben).

Am Entwurf des Formulars brauchen Sie keine änderungen vorzunehmen, wir kümmern uns nur um den Code.

Wie zuvor soll beim öffnen des Formulars die Liste der Kategorien erscheinen. Beim Klick auf eines der Plus-Zeichen sollen die zur jeweiligen Kategorie gehörenden Artikel im Treeview angezeigt werden.

Dazu implementieren wir ein Ereignis der Klasse Form_USys_pTV_TreeView. Zu diesem Zweck wechseln Sie zum VBA-Fenster des Klassenmoduls des Formulars frmKategorienArtikel, wählen im linken Kombinationsfeld des Fensters den Eintrag objTreeView und im rechten Kombinationsfeld den Eintrag ItemOpened aus (s. Bild 5).

Implementieren einer Ereignisprozedur des picoware-Treeviews

Bild 5: Implementieren einer Ereignisprozedur des picoware-Treeviews

Dies legt die folgende Ereignisprozedur an:

Private Sub objTreeView_ItemOpened(Context As  USys_pCT_Context, ByVal RefPath As String)
End Sub

Die Ereignisprozedur wird immer ausgelöst, wenn der Benutzer auf einen der Einträge im Treeview klickt. Dies können Sie ausprobieren, indem Sie einen Haltepunkt für die erste Zeile der Prozedur festlegen und auf eine der Kategorien im Beispielformular frmKategorienArtikel klicken.

Nun wollen wir die Zeilen hinzufügen, die dafür sorgen, dass beim Anklicken des Plus-Zeichens einer Kategorie die untergeordneten Artikel im Treeview erscheinen. Diese Prozedur sieht wie in Listing 2 aus.

Private Sub objTreeView_ItemOpened(Context As USys_pCT_Context, ByVal RefPath As String)
    Dim strSQL As String
    Dim lngReference As Long
    lngReference = Context.SettingLong("Reference", -1)
    strSQL = "Select ArtikelID As Reference, ''''Artikel'''' As RefContext, Artikelname As Caption " _
          & "FROM tblArtikel WHERE KategorieID = " & lngReference
    objTreeView.AddSQL strSQL, RefPath
End Sub

Listing 2: Ereignisprozedur, welche die Artikel für die angeklickte Kategorie einblendet

Eine zusätzliche Zeile deklariert eine Variable namens lngReference mit dem Datentyp Long, eine weitere füllt diese mit einem Wert, den der Ausdruck Context.SettingLong(“Reference”, -1) liefert. Context ist einer der beiden Parameter, der beim Auslösen der Ereignisprozedur übergeben wird.

Dieser liefert einige Informationen rund um das angeklickte Element und auch einige mögliche Methoden. In diesem Fall verwenden wir die Funktion SettingLong, um den Wert des Attributs Reference zu ermitteln. Dabei handelt es sich um den Wert des angeklickten Elements, der in der SQL-Anweisung, welche dieses Element definiert, für das Feld mit dem ALIAS-Namen Reference übergeben wurde – in diesem Fall also der Wert des Feldes KategorieID für den angeklickten Eintrag.

Diesen Wert speichern wir in der Variablen lngReference und nutzen ihn in der nachfolgend zusammengestellten SQL-Anweisung als Vergleichswert des Kriteriums. Für die KategorieID mit dem Wert 5 sieht die SQL-Abfrage etwa so aus:

Select ArtikelID As Reference, 
''''Artikel'''' As RefContext, 
Artikelname As Caption 
FROM tblArtikel 
WHERE KategorieID = 5

Danach folgt dann auch für die Elemente dieser zweiten Ebene der Aufruf der Methode AddSQL des Objekts aus objTreeView. Diesmal verwenden wir allerdings auch den zweiten Parameter dieser Methode und übergeben dieser den unveränderten Wert des zweiten Parameters der Ereignisprozedur namens RefPath.

Was enthält dieser Parameter Dies erfahren wir wieder, indem wir einen Haltepunkt setzen und dann nach dem Abarbeiten der ersten Zeile mit der Maus über den Parameter fahren (s. Bild 6).

Ermitteln eines der Parameter der Ereignisprozedur

Bild 6: Ermitteln eines der Parameter der Ereignisprozedur

Es handelt sich also um die Zeichenfolge /5/, wobei der Wert 5 wieder dem Referenzwert des auslösenden Elements entspricht. Diesen übergeben wir der Methode AddSQL, damit diese weiß, an welches Element sie die neuen Elemente anfügen soll.

Das Ergebnis der neu hinzugefügten Funktion finden Sie schließlich in der Formularansicht aus Bild 7.

Treeview mit zwei Ebenen

Bild 7: Treeview mit zwei Ebenen

öffnen-Klick auf der zweiten Ebene

Was geschieht aber nun, wenn wir auf das Plus-Zeichen eines der Elemente der zweiten Ebene klicken Dies bewirkt in den meisten Fällen nichts, außer dass das Plus-Zeichen für den angeklickten Eintrag verschwindet.

Es kann aber auch sein, dass sich eine weitere Ebene öffnet, welche nochmal die gleichen Artikel wie die zweite Ebene enthält, von der aus ein Element aufgeklappt wurde. Aber was geschieht dort im Detail Da es nur eine Ereignisprozedur namens ItemOpened für alle Elemente des Treeviews gibt, wird diese natürlich auch beim Klicken auf das Plus-Zeichen eines der Elemente der zweiten Ebene mit den Artikeln ausgelöst.

Der Parameter RefPath liefert nun einen Wert wie etwa /5/52/, wobei der Wert 5 dem Referenzwert für das Element der ersten Ebene und 52 dem Referenzwert für das Element der zweiten Ebene, also des Artikels, entspricht. Der Ausdruck Context.SettingLong(“Reference”, -1) liefert nun den Primärschlüsselwert des angeklickten Elements der zweiten Ebene.

Die SQL-Anweisung ermittelt nun alle Elemente der Tabelle tblArtikel, deren Feld KategorieID den Wert 52 enthält – was in diesem Fall keine Ergebnisse liefert. Hätten wir einen Artikel angeklickt, dessen Wert im Feld ArtikelID auch im Feld KategorieID eines der Datensätze der Tabelle tblKategorien vorkommt, hätte dies alle Datensätze der Tabelle tblArtikel zu dieser Kategorie geliefert. Das soll aber natürlich so nicht geschehen.

Also erweitern wir die Ereignisprozedur objTreeView_Item-Opened wie in Listing 3. Sie erinnern sich, dass wir gleich in der SQL-Anweisung für das Hinzufügen der Kategorie-Elemente ein Feld mit dem ALIAS-Namen RefContext und dem Wert ””Kategorie”” übergeben haben. Diesen nutzen wir nun, um zu ermitteln, ob der Benutzer auf eines der Elemente der ersten Ebene, also auf eine Kategorie geklickt hat oder auf eines der Objekte einer anderen Ebene.

Private Sub objTreeView_ItemOpened(Context As USys_pCT_Context, ByVal RefPath As String)
     Dim strSQL As String
     Dim lngReference As Long
     Dim strRefContext As String
     strRefContext = Context.SettingNz("RefContext", "Root")
     Select Case strRefContext
         Case "Kategorie"
             lngReference = Context.SettingLong("Reference", -1)
             strSQL = "Select ArtikelID As Reference, ''''Artikel'''' As RefContext, Artikelname As Caption " _
                    & "FROM tblArtikel WHERE KategorieID = " & lngReference
             objTreeView.AddSQL strSQL, RefPath
     End Select
End Sub

Listing 3: Neue Version der Ereignisprozedur, welche die Artikel für die angeklickte Kategorie einblendet

Wir ermitteln diesen Wert über den Ausdruck Context.SettingNz(“RefContext”, “Root”), was im Falle des Erweiterns eines der Elemente der ersten Ebene den Wert Kategorie zurückliefert (ist kein RefContext angegeben, lautet der Wert Root). Diesen speichern wir in der Variablen strRefContext und prüfen ihn in einer Select Case-Bedingung. Diese hat nur einen Case-Zweig, der im Falle des Wertes Kategorie aufgerufen wird und die Einträge der Tabelle tblArtikel für die angegebene Kategorie hinzufügt. Wenn wir nun also auf das Plus-Zeichen eines Eintrags der zweiten Ebene, also eines Artikels klicken, liefert Context.SettingNz(“RefContext”, “Root”) den Wert Artikel, für den es keinen Case-Zweig in der Select Case-Variablen gibt. Dementsprechend geschieht auch nichts weiter.

Erst, wenn Sie noch weitere Elemente unterhalb der Artikel-Elemente anzeigen wollten, müssten Sie einen weiteren Case-Zweig zur Select Case-Bedingung hinzufügen.

Unnötige Plus-Zeichen ausblenden

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

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar