TreeView: Neue Elemente anlegen

Das Anlegen neuer Elemente in einem TreeView-Steuerelement, das die Daten aus ein oder mehreren Tabellen anzeigt, ist nicht immer trivial. Soll das Anlegen über ein Detailformular erfolgen und der Eintrag dann zum TreeView hinzugefügt werden Oder reicht vielleicht die Bezeichnung des neuen Elements zunächst aus, um dieses zum TreeView hinzuzufügen – dann wäre eine InputBox oder die direkte Angabe im TreeView eine Lösung. Dieser Beitrag zeigt verschiedene Möglichkeiten, um neue Elemente anzulegen.

Varianten

Wer ein TreeView-Steuerelement einsetzt, beabsichtigt zunächst die Anzeige hierarchischer Daten in übersichtlicher Form.

Gelegentlich kommt der Wunsch hinzu, die Daten direkt über das TreeView-Steuerelement zu bearbeiten oder zumindest von dort aus die zur Bearbeitung notwendigen Formulare zu öffnen beziehungsweise Unterformulare mit den entsprechenden Daten zu füllen. In diesem Beitrag schauen wir uns an, wie Sie neue Elemente zu den verschiedenen TreeView-Steuerelementen hinzufügen. Dabei gibt es im Wesentlichen die folgenden Varianten:

  • Die Elemente sollen nur im TreeView-Steuerelement angezeigt werden. In diesem Fall reicht es, ein neues Element hinzuzufügen und dem Benutzer die Möglichkeit zu bieten, die Beschriftung anzupassen. änderungen an den Daten des neu hinzugefügten Datensatzes kann er später vornehmen.
  • Diese Variante können Sie auch mithilfe einer InputBox unterstützen. Beim Anlegen eines neuen Eintrags zeigen Sie dabei eine InputBox zum Erfassen der Bezeichnung des Elements an und fügen das neue Element dann gleich unter Angabe der Bezeichnung zum TreeView-Steuerelement hinzu.
  • Die Daten sind komplexer, das heißt, dass etwa die im TreeView-Steuer-element angezeigte Bezeichnung sich aus den Daten mehrerer Felder zusammensetzt. In diesem Fall muss zwingend ein Formular geöffnet werden, um den dem neuen TreeView-Element zugrunde liegenden Datensatz anzulegen. Erst dann kann die Beschriftung für den Eintrag im TreeView-Steuerelement aktualisiert werden.

Letztere Variante kann beispielsweise in den folgenden beiden Ausprägungen auftauchen:

  • Das Formular mit dem TreeView-Steuerelement zeigt in einem Unterformular jeweils die Daten des aktuell im TreeView-Steuerelement markierten Elements an.
  • Die Einträge im TreeView-Element werden jeweils in eigens dazu geöffneten Formularen angezeigt – etwa per Mausklick, Doppelklick, Kontextmenü oder Schaltfläche.

Der Fall mit dem Unterformular ist einigermaßen kompliziert, denn: Was soll beim Anlegen eines neuen Datensatzes zuerst geschehen – soll erst ein neues Element im TreeView-Steuerelement angelegt werden oder erst ein neuer Datensatz Und wann und wie werden die änderungen im Datensatz in das TreeView-Steuerelement übertragen Diese und andere Fragen beantworten wir auf den folgenden Seiten.

Speichern der Daten

Eines haben alle Varianten gemeinsam: Es soll immer ein Datensatz zu der Tabelle hinzugefügt werden, welche die Elemente der aktuellen Ebene des TreeView-Steuerelements liefert (wobei eine Ebene natürlich auch Elemente aus verschiedenen Tabellen enthalten kann). Gleichzeitig soll ein neuer Eintrag zum TreeView-Steuerelement hinzugefügt werden.

Auslösen des Anlegevorgangs

Einen neuen Eintrag können Sie auf verschiedene Arten zum TreeView-Steuerelement hinzufügen. Die komfortabelste Möglichkeit ist sicher der Einsatz von Kontextmenüs, da diese immer nur die für das jeweilige Element verfügbaren Befehle anzeigen.

Wenn das TreeView-Steuerelement nur Daten aus einer reflexiv verknüpften Tabelle anzeigt, reicht sicher auch eine Schaltfläche außerhalb des TreeView-Steuerelements aus, um ein neues Element zum aktuell markierten Element hinzuzufügen.

Beim Hinzufügen eines Elements muss aber in jedem Fall feststehen, unterhalb welches anderen Elements das neue Element angelegt werden soll – oder ob dieses als neues Root-Element ohne übergeordnetes Element zu erstellen ist.

Bei TreeView-Steuerelementen, die Daten aus mehreren hierarchisch verknüpften Tabellen anzeigen, müssen Sie außerdem wissen, zu welcher Tabelle das neue Element hinzugefügt werden soll.

Dies ergibt sich aber in der Regel aus dem Aufbau der Tabellen und geschieht somit in Abhängigkeit vom übergeordneten Element. Interessant wird dies, wenn Sie ein TreeView-Steuerelement mit den Daten aus Tabellen mit gemischten Beziehungen füllen – also sowohl aus reflexiv verknüpften Tabellen als auch aus anderen hierarchisch verknüpften Tabellen. Dies ist beispielsweise der Fall, wenn Sie Verzeichnisse und Dateien abbilden. Jedes Verzeichnis kann Verzeichnisse und Dateien enthalten, also müssen Sie für jedes Verzeichnis auch Befehle zum Hinzufügen von Verzeichnissen und Dateien bereitstellen.

Beispiel Stückliste

Ein Beispiel, dass sowohl reflexiv verknüpfte Elemente als auch 1:n-Verknüpfungen enthält, ist die Stückliste. Der Beitrag Stücklisten (www.access-im-unternehmen.de/923) liefert das Beispiel für den vorliegenden Beitrag. Dort finden Sie Baugruppen, die aus weiteren Baugruppen oder aus Einzelteilen bestehen können. Ein Einzelteil hingegen ist eine atomare Einheit und kann nicht in weitere Elemente aufgeteilt werden.

Dieses Beispiel enthält sowohl Daten aus einer Tabelle, die prinzipiell nur die Bezeichnung der Elemente speichert (Baugruppen) also auch solche, die mehrere Attribute erfordern (Einzelteile). Wir können uns im Rahmen dieses Beitrags also an diesem Beispiel austoben.

Wichtig ist an dieser Stelle, dass das TreeView-Steuerelement mit einer Variablen namens objTreeView referenziert wird, die gleich beim Laden des Formulars gefüllt wird. Daher können wir in den folgenden Beispielprozeduren jeweils direkt auf objTreeview zugreifen und auch für dieses Objekt die benötigten Ereignisprozeduren implementieren.

Einfache Elemente hinzufügen

Wie oben erläutert, ist der einfachste Fall für das Hinzufügen von Elementen zu einem TreeView-Steuerelement der, bei dem im ersten Schritt allein die Bezeichnung des jeweiligen Elements angegeben und dann sowohl in einem neuen Element im TreeView-Steuerelement angezeigt als auch in der zugrunde liegenden Tabelle gespeichert wird. Im Beispiel handelt es sich dabei um die Baugruppen. Wir schauen uns zwei Methoden an – die mit der InputBox und die mit dem direkten Anlegen im TreeView-Steuerelement.

Element per InputBox hinzufügen

Um Elemente einfach hinzuzufügen, haben wir einige Schaltfflächen im oberen Bereich des Beispielformulars mit dem TreeView-Steuerelement untergebracht (s. Bild 1). Wie das TreeView-Steuerelement gefüllt wird, erfahren Sie im oben bereits erwähnten Beitrag Stücklisten.

Schaltflächen zum Anlegen neuer Elemente

Bild 1: Schaltflächen zum Anlegen neuer Elemente

Die Schaltfläche cmdNeueBaugruppeI soll eine InputBox anzeigen, welche die Bezeichnung der neuen Baugruppe abfragt. Anschließend soll der Eintrag an der gewünschten Stelle im TreeView-Steuerelement angelegt und in der Datenbank gespeichert werden.

Die Prozedur, die durch das Ereignis Beim Klicken der Schaltfläche ausgelöst wird, finden Sie in Listing 1. Die Prozedur speichert zunächst einen mit der Eigenschaft SelectedItem ermittelten Verweis auf das aktuell markierte Element im TreeView-Steuerelement in der Variablen objNode. Sie ermittelt den Key-Wert des Elements und speichert diesen in der Variablen strKey. Nun benötigen wir ein paar Informationen, die sich in der Key-Eigenschaft befinden. Der Inhalt von Key ist normalerweise auf eine von zwei Arten aufgebaut:

Private Sub cmdNeueBaugruppeI_Click()
     Dim objNode As MSComctlLib.Node
     Dim objNodeNeu As MSComctlLib.Node
     Dim strKey As String
     Dim strTyp As String
     Dim lngID As Long
     Dim lngNeuID As Long
     Dim strBezeichnung As String
     Dim db As DAO.Database
     Set objNode = objTreeView.SelectedItem
     strKey = objNode.Key
     KeyParsen strKey, strTyp, lngID
     strBezeichnung = InputBox("Geben Sie die Bezeichnung der neuen Baugruppe an.", _
         "Neue Baugruppe", "[Neue Baugruppe]")
     If Len(strBezeichnung) > 0 And Not strBezeichnung = "[Neue Baugruppe]" Then
         Set db = CurrentDb
         Select Case strTyp
             Case "r"
                 db.Execute "INSERT INTO tblBaugruppen(Baugruppe, IstProdukt) VALUES(''" _
                     & strBezeichnung & "'', -1)", dbFailOnError
                 lngNeuID = db.OpenRecordset("SELECT @@IDENTITY").Fields(0)
                 Set objNodeNeu = objTreeView.Nodes.Add(objNode.Key, tvwChild, objNode.Key _
                     & "|p" & lngNeuID, strBezeichnung, "elements_hierarchy")
             Case "p", "b"
                 db.Execute "INSERT INTO tblBaugruppen(Baugruppe, IstProdukt) VALUES(''" _
                     & strBezeichnung & "'', 0)", dbFailOnError
                 lngNeuID = db.OpenRecordset("SELECT @@IDENTITY").Fields(0)
                 db.Execute "INSERT INTO tblBaugruppenzuordnungen(BaugruppeVaterID, " _
                     & "BaugruppeKindID) VALUES(" & lngID & ", " _
                     & lngNeuID & ")", dbFailOnError
                 Set objNodeNeu = objTreeView.Nodes.Add(objNode.Key, tvwChild, objNode.Key _
                     & "|p" & lngNeuID, strBezeichnung, "gearwheels")
         End Select
         objNode.Expanded = True
         objTreeView.SelectedItem = objNodeNeu
         Me!ctlTreeView.SetFocus
     End If
End Sub

Listing 1: Anlegen eines neuen TreeView-Elements per InputBox

  • Buchstabe, der die Art des Elements charakterisiert (in diesem Beispiel r für Root, p für Produkt, b für Baugruppe oder e für Einzelteil)
  • Zahl, die den Wert des Primärschlüsselfeldes des Datensatzes enthält, den das Element repräsentiert

Der Key könnte also etwa r0, p123 oder b13 lauten. Im vorliegenden Fall haben wir es allerdings mit einem TreeView-Steuerelement zu tun, das Daten aus einer reflexiven Beziehung enthält. Ein Element, etwa eine Baugruppe oder ein Einzelteil, könnte also mehrfach im TreeView-Steuerelement vorkommen. Jeder Key darf aber nur einmal vorkommen, sodass wir die Eindeutigkeit anderweitig sicherstellen müssen. In diesem Fall besteht der Key-Wert aus einer Abfolge aller Key-Werte in der Hierarchie vom Root-Element bis zum aktuellen Element, und zwar getrennt durch ein spezielles Zeichen wie etwa das Pipe-Zeichen: r0|p1|b2|b27|e12.

Um aus einem solchen Ausdruck den kennzeichnenden Buchstaben für den Typ des Elements sowie den Primärschlüsselwert zu ermitteln, haben wir eine kleine Hilfsprozedur entwickelt.

Diese heißt KeyParsen und sieht wie in Listing 2 aus (siehe Modul mdlTools). Die Prozedur erwartet drei Parameter. strKey übergibt den Key-Wert und die Parameter strTyp und lngID sind Rückgabeparameter, die in der Prozedur KeyParsen gefüllt werden.

Public Sub KeyParsen(ByVal strKey As String, strTyp As String, lngID As Long)
     Dim lngPosPipe As Long
     lngPosPipe = InStrRev(strKey, "|")
     If lngPosPipe > 0 Then
         strKey = Mid(strKey, lngPosPipe + 1)
     End If
     strTyp = Left(strKey, 1)
     lngID = Mid(strKey, 2)
End Sub

Listing 2: Die Funktion KeyParsen

KeyParsen ermittelt zunächst mit der InStrRev-Funktion die Position des hintersten Pipe-Zeichens. Ist diese größer 0, schneidet die Prozedur den Teil vor diesem Trennzeichen ab, sodass strKey anschließend nur noch den Key des aktuellen Elements enthält. Falls kein Pipe-Zeichen vorliegt, scheint es sich bereits um einen einfachen Key-Wert zu handeln (entweder, weil es ein Root-Element ist oder weil alle Key-Werte einfache Key-Werte sind). strType wird dann mit dem ersten Zeichen aus strKey gefüllt, lngID mit der darauf folgenden Zeichenkette (dies funktioniert nur mit TreeView-Steuerelementen, die nur einfache Buchstaben verwenden – sollten Sie mehr Buchstaben benötigen, müssen Sie den Mechanismus auf zwei oder mehr Buchstaben zu Beginn des Key-Wertes umstellen). Normalerweise sollten jedoch 26 verschiedene Typen ausreichen, was den Buchstaben des Alphabets entspricht (der Key-Wert muss immer mit einem Buchstaben beginnen).

Die Prozedur cmdNeueBaugruppeI_Click ruft also die Routine KeyParsen auf, um strTyp und lngID einzulesen. Dann ermittelt sie per InputBox den Namen der neuen Baugruppe (s. Bild 2) und speichert das Ergebnis in der Variablen strBezeichnung. Hat der Benutzer einen Wert ungleich dem Standardwert [Neue Baugruppe] eingegeben, erfolgen die weiteren Schritte. Die Prozedur prüft anhand von strKey den Typ des übergeordneten Elements. Wenn es das Root-Element ist (r), legt die Prozedur einen neuen Datensatz in der Tabelle tblBaugruppen an und stellt den Wert von IstProdukt auf -1 (True) ein. Die folgende Anweisung ermittelt den Primärschlüsselwert des neuen Datensatzes und speichert diesen in lngNeuID. Damit kann sie nun das neue Element im TreeView-Steuer-element anlegen. Dabei verwendet sie den Key des mit objNode referenzierten Elements als Bezugspunkt und legt das neue Element als Kind-Element an. Der Key wird aus dem Key des übergeordneten Elements, dem Pipe-Zeichen, dem Buchstaben p (für Produkt) und dem Primärschlüsselwert gebildet (für TreeViews ohne reflexive Beziehung reichen der Buchstabe und der Primärschlüsselwert aus). Als Bezeichnung kommt schließlich der per InputBox ermittelte Ausdruck zum Einsatz.

Anlegen eines neuen Elements per InputBox

Bild 2: Anlegen eines neuen Elements per InputBox

Sollte die Baugruppe unterhalb einer als Produkt gekennzeichneten Baugruppe oder einer einfachen Baugruppe (strKey gleich p oder b) angelegt worden sein, ist ein zusätzlicher Schritt nötig. Zunächst legt die Prozedur wieder den Datensatz in tblBaugruppen an (diesmal mit dem Wert 0/False im Feld IstProdukt) und erstellt das Element im TreeView-Steuerelement unterhalb des aktuellen Elements. Um die Abhängigkeit von dem übergeordneten Element nach dem Schließen und erneuten öffnen des Formulars wiederherstellen zu können, muss dieser noch in Form eines Datensatzes in der reflexiven Verknüpfungstabelle tblBaugruppenZuordnungen gespeichert werden.

Im Anschluss an diese Schritte stellt die Prozedur noch den Wert der Eigenschaft Expanded des übergeordneten Node-Objekts auf True ein und klappt damit die untergeordneten Elemente aus. Das neue Element wird als aktuelles Element markiert und das TreeView-Steuerelement erhält den Fokus, damit die Markierung des aktuellen Steuerelements gut zu erkennen ist (sonst wird dieses nur schwach grau dargestellt).

Element direkt hinzufügen

Alternativ zur ersten Methode können Sie auch direkt ein neues Element unterhalb des aktuell markierten Elements anlegen und dieses gleich zum Bearbeiten durch den Benutzer vorbereiten (s. Bild 3).

Direktes Anlegen eines neuen Elements

Bild 3: Direktes Anlegen eines neuen Elements

Die Prozedur, die durch einen Mausklick auf die Schaltfläche cmdNeueBaugruppeII ausgelöst wird, finden Sie in Auszügen in Listing 3. Warum in Auszügen Weil die Prozedur fast genauso aufgebaut ist wie die Prozedur aus dem vorherigen Beispiel.

Private Sub cmdNeueBaugruppeII_Click()
     ...
     Select Case strTyp
         Case "r"
             db.Execute "INSERT INTO tblBaugruppen(Baugruppe, IstProdukt) " _
                 & "VALUES(''[Neue Baugruppe]'', -1)", dbFailOnError
             ...
         Case "p", "b"
             db.Execute "INSERT INTO tblBaugruppen(Baugruppe, IstProdukt) " _
                 & "VALUES(''[Neue Baugruppe]'', 0)", dbFailOnError
             ...
     End Select
     ...
     objTreeView.StartLabelEdit
End Sub

Listing 3: Diese Prozedur legt ein neues Node-Objekt an und bietet seine Bearbeitung an.

Sie ermittelt die Informationen zum übergeordneten Element, prüft den Typ dieses Elements und ruft dann eine von zwei möglichen INSERT INTO-Anweisungen innerhalb einer Select Case-Bedingung auf. Dort gibt es jedoch den Unterschied, dass die Prozedur nicht zuvor die Bezeichnung des neu hinzuzufügenden Elements abgefragt hat. Diese soll der Benutzer aber ja auch später eingeben, daher verwendet die INSERT INTO zunächst den Wert [Neue Baugruppe] als Wert für die Bezeichnung des neuen Node-Elements.

Nach dem Anlegen des neuen Elements und dem Erstellen des entsprechenden Datensatzes in der Tabelle tblBaugruppen (beziehungsweise in tblBaugruppenZuordnungen) markiert die Prozedur das neue Element noch durch Einstellen der Eigenschaft StartLabelEdit zum Bearbeiten.

Ereignis beim ändern des neuen Eintrags

Da der Eintrag nun hinzugefügt und zum ändern durch den Benutzer vorbereitet wurde, braucht der Benutzer nur noch die neue Bezeichnung der Baugruppe einzufügen und die Eingabe zu bestätigen. Durch diese Aktion allein ändert er jedoch nur den angezeigten Wert. Nach dem Schließen und öffnen des Formulars zeigt dieses wieder den vorherigen Wert (hier [Neue Baugruppe]) an.

Typischerweise wird der Benutzer den neuen Eintrag aber direkt nach dem Anlegen auch anpassen. Damit die vorgenommenen änderungen auch in der Datenbank gespeichert werden, legen Sie eine Ereignisprozedur an, die durch das Ereignis AfterLabelEdit des TreeView-Steuerelements ausgelöst wird (s. Listing 4). Die Prozedur referenziert zunächst das aktuell markierte Element im TreeView-Steuerelement mit der Variablen objNode. Dann ermittelt sie den Key-Wert und speichert diesen in der Variablen strKey. Ein Aufruf der Prozedur KeyParsen ermittelt aus dem Key den Typ des Elements sowie den Primärschlüsselwert des betroffenen Datensatzes.

Private Sub objTreeView_AfterLabelEdit(Cancel As Integer, NewString As String)
     Dim objNode As MSComctlLib.Node
     Dim strKey As String
     Dim strTyp As String
     Dim lngID As Long
     Dim strBezeichnung As String
     Dim db As DAO.Database
     Set objNode = objTreeView.SelectedItem
     strKey = objNode.Key
     KeyParsen strKey, strTyp, lngID
     strBezeichnung = NewString
     Set db = CurrentDb
     Select Case strTyp
         Case "p", "b"
             db.Execute "UPDATE tblBaugruppen SET Baugruppe = ''" & strBezeichnung _
                 & "'' WHERE BaugruppeID = " & lngID, dbFailOnError
     End Select
     objNode.Expanded = True
     objTreeView.SelectedItem = objNode
     Me!ctlTreeView.SetFocus
End Sub

Listing 4: Prozedur, die nach dem ändern eines der Einträge im TreeView-Steuerelement ausgelöst wird

Der Parameter NewString der Prozedur objTreeView_AfterLabelEdit liefert die durch den Benutzer eingegebene neue Bezeichnung. Hat das Element den Typ p oder b, aktualisiert die Prozedur den Wert des Feldes Baugruppe des Datensatzes mit dem Primärschlüsselwert aus lngID mit der neu eingegebenen Bezeichnung. Anschließend werden eventuelle Unterelemente angezeigt, das Element markiert und der Fokus auf das TreeView-Steuerelement gelegt.

Elemente mit Details anlegen

Die Einzelteile, aus denen eine Baugruppe besteht, enthalten einige weitere Eigenschaften. Angenommen, diese werden für weitere Aufgaben benötigt, reicht es natürlich nicht aus, Einzelteile einfach wie bei den beiden oben erwähnten Methoden anzulegen – also nur die Bezeichnung anzugeben und den Rest irgendwann später nachzutragen.

Erfahrungsgemäß geschieht dies nämlich oft nicht zeitnah, was zu einem lückenhaften Datenbestand führt.

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