Access Bedienungsanleitung: Hierarchien/Baumstrukturen/Treeviews für Anfänger

Aus DBWiki
Wechseln zu: Navigation, Suche

Hierarchien in Access mit Treeviews darstellen

Um Hierarchien, z.B. "Mitarbeiter und Ihre Vorgesetzten", baumartig und aufklappbar darzustellen (wie die Laufwerke und Ordner im Windows-Explorer) kann man das Treeview-Control nutzen.

Nötige Vorbereitungen

  • Die Daten müssen in einer Tabelle vorliegen, die folgende Struktur hat:
ElementNr ElementBezeichnung ElementNr_Parent
1 Meier  
2 Schulze 1
3 Schmitt 1
4 Müller  
5 Schäfer 3
6 Lorenz 5
7 Höhler 5
  • Jeder Datensatz hat eine eindeutige Nummer. Datensätze auf höchster Ebene haben keinen Eintrag im Parent-Kennzeichen. Datensätze, die anderen Datensätzen untergeordnet sind, haben die ID dieses Datensatzes im Parent-Kennzeichen. Die Parent-Kennzeichen dürfen nur aus Nummern bestehen, die in der Tabelle als ID vorkommen.
  • Diese Struktur ist u.U. nicht vorhanden. Es ist aber möglich, diese Struktur aus linearen Daten herauszulesen. Damit sind Tabellen gemeint, die die Hierarchie durch folgenden Aufbau deutlich machen:
Ebene1 Ebene2 Ebene3 Ebene4 ... EbeneX
  • In der MDB muß ein Verweis auf die mscomctl.ocx (Microsoft Windows Common Controls) gelegt sein, die das Treeview-Control enthält. Diese Datei sollte sich im System32-Ordner des Windows-Verzeichnisses befinden. Zum Anlegen eines Verweises auf diese Datei geht man in Access wie folgt vor:
    • Neues Modul anlegen (es wird später auch für diversen Code gebraucht)
    • In VBA den Menübefehl Extras/Verweise" aufrufen
    • Die "Microsoft WIndows Common Controls" in der Liste suchen.
      • wenn vorhanden und bereits angekreuzt, ist der Verweis bereits gesetzt
      • wenn vorhanden und nicht angekreut, bitte anklicken
      • wenn nicht vorhanden oder als "NICHT GEFUNDEN" deklariert, mit Hilfe des Durchsuchen-Buttons die Datei aus dem Windows-System32-Ordner einbinden.

Einbauen des Treeview-Controls in ein Formular

  • In einem in Entwurfsansicht befindlichen Formular kann nun über Einfügen/ActiveX-Steuerelement das "Microsoft Treeview Control" ausgewählt und in gewünschter Größe auf dem Formular eingefügt werden.
  • Per Doppelklick auf das Controls können nun einige generelle Parameter für das Treeview gesetzt werden, die v.a. das Aussehen und das Verhalten des Treeviews beeinflussen. S. dazu auch Benutzerdefinierbare Einstellungen des Treeviews
  • Das Treeview hat im Unterschied zu anderen Steuerelementen keine Datenherkunft-Eigenschaft. Die anzuzeigenden Daten werden über eine VBA-Routine in das Treeview geschrieben. Dies sollte in einem geeigneten Ereignis geschehen, z.B. BeimÖffnen des Formulares oder BeimKlicken auf einen Button.

Die VBA-Routine stammt von Microsoft (Quelle im Code). Ich habe sie ergänzt um die Anzeige mehrerer Felder in der Astbeschriftung.

Beispiel

'TreeView mit den Daten bestücken
Const strTableQueryName = "Mitarbeitertabelle"
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset(strTableQueryName, _
          dbOpenDynaset, dbReadOnly)
AddBranch Me.Name, "acxTreeView", rst, "[ElementNr_Parent]", _
          "[ElementNr]", "[ElementNr];[Element_Bezeichnung]"
  • Die Prozedur AddBranch muß nun noch definiert werden, indem folgender Code in das bereits erstellte Modul kopiert wird und ggf. noch ein Verweis auf die DAO 3.6 Library gesetzt wird:
'================= AddBranch Sub Procedure ======================
'Rekursive Sub zur Generierung von TreeView-Ästen.
'
' Quelle: Microsoft Knowledge Base, http://support.microsoft.com,
' erweitert durch Martin Seip.
'      Recursive Procedure to add branches to TreeView Control
'Requires:
'   ActiveX Control:  TreeView Control
'              Name:  wie übergeben
'Parameters:
'          Formname:  Name des Forms, in dem das TreeView-Element steht
'       Controlname:  Name des TreeView-Elements (Neu durch M. Seip)
'               rst:  Self-referencing DAO-Recordset containing the data
'   strPointerField:  Name of field pointing to parent's primary key
'        strIDField:  Name of parent's primary key field
'      strTextField:  Name of field containing text to be displayed
'erweitert duch M. Seip: Angabe mehrerer Felder durch ; getrennt möglich;
'                     werden dann verkettet mit " / " und gemeinsam ausgegeben
'                     z.B. "1300 / Sales"
'=============================================================
' Anmerkung Martin Seip 05.04.2002:
' Die Add-Methode des TreeView.Node-Objektes interpretiert die
' übergebene "Mutterposition" (Variant) als *Key*wert, wenn sie als
' String übrgeben wird und als *Index*wert (!), wenn sie numerisch ist.
' Ein CStr reicht leider nicht aus, daher wurde an die BPs als Key der
' Buchstabe a vorangestellt, so daß eine Stringvariable entsteht.
 
Sub AddBranch(strFormname As String, strControlname As String, _
              rst As DAO.Recordset, strPointerField As String, _
              strIDField As String, strTextField As String, _
              Optional varReportToID As Variant)
On Error GoTo errAddBranch
Dim nodParent
Dim strCriteria As String, strText As String, strKey As String
Dim bk            As String
Dim txtfld        As Variant
'Feststellen der Mutterposition
If IsMissing(varReportToID) Then  ' Erste EBene
  strCriteria = strPointerField & " Is Null"
Else  ' Töchter suchen
  strCriteria = BuildCriteria(strPointerField, _
                rst.Fields(strPointerField).Type, "=" & varReportToID)
  'Mutternode speichern
  Set nodParent = Forms(strFormname). _
                  Controls(strControlname).Nodes("a" & varReportToID)
End If
'für jeden Tochtersatz
rst.FindFirst strCriteria
Do Until rst.NoMatch
  'Anzuzeigende Texte und Keywert (String, s.o.!!) bauen
  strText = ""
  For Each txtfld In Split(strTextField, ";")
    strText = strText & " / " & rst(txtfld)
  Next txtfld
  strText = Right$(strText, Len(strText) - 3)
  strKey = "a" & rst(strIDField)
  If Not IsMissing(varReportToID) Then  'Tochternode einfügen
    Forms(strFormname).Controls(strControlname).Nodes. _
    Add nodParent, tvwChild, strKey, strText
  Else    ' Node auf erster Ebene einfügen
    Forms(strFormname).Controls(strControlname).Nodes. _
    Add , , strKey, strText
  End If
  ' Position zwischenspeichern
  bk = rst.Bookmark
  'Töchternodes einbauen (Rekursion)
  AddBranch strFormname, strControlname, rst, _
            strPointerField, strIDField, strTextField, rst(strIDField)
  rst.Bookmark = bk     ' Aufsuchen der letzten Positionen
  rst.FindNext strCriteria   ' nächste BP
Loop
exitAddBranch:
Exit Sub
'--------------------------Error Trapping --------------------------
errAddBranch:
MsgBox "Can't add child:  " & Err.Description, _
       vbCritical, "AddBranch Error:"
'Stop
'Resume exitAddBranch
Resume Next
End Sub
  • Nach Kompilierung (Debuggen/Kompilieren von [Modulname]) sollten sich keine Fehler ergeben.
  • Der Baum wird nun sofort neu gezeichnet, sobald das auslösende Ereignis eintritt (Formular wird geöffnet, Button wird angeklickt etc.)

Beispielhafte Darstellung des Endergebnisses

Unter Nutzung der obigen Mitarbeitertabelle ergibt das Treeview dann folgendes Bild:

Beispielbaum

In den Ästen werden hier Nummer und Bezeichnug angezeigt, da im Beispiel das Argument strTextField mit zwei Feldern bestückt wurde:

AddBranch strFormname:=Me.Name, strControlname:="acxTreeView", _
rst:=rst, strPointerField:="[ElementNr_Parent]", _
strIDField:="[ElementNr]", strTextField:="[ElementNr];[ElementBezeichnung]"

Sollte dies unerwünscht sein, kann natürlich auch nur ein Feld, z.B. die Bezeichnung, als strTextField eingetragen werden.

Drucken von Treeviews / Treeviews in Berichten

Treeviews lassen sich als Bestandteil des Formulars oder Berichts ausdrucken "as is", d.h. mit ihrem momentan sichtbaren Bereich. Trees, die länger sind als das Steuerelement derzeit anzeigt, werden dabei jedoch abgeschnitten. Es gibt daher nur die Möglichkeit, den Tree Ast für Ast in eine Tabelle zu schreiben und dabei die Einrückungen durch hinreichend viele Leerzeichen zu simulieren. Die baumartigen Striche gehen dabei verloren. Es ist bisher kein Weg bekannt, den Tree "baumartig" mit Linien auszugeben. Für die Druckausgabe kann man sich mit folgender Routine, die den Baum ähnlich wie eine Hardcopy in die angegebene Tabelle "druckt", behelfen. Diese Tabelle wird dabei als Basis für einen gewöhnlichen Bericht genutzt. Aufruf der Routine dann z.B. beim Ereignis BeimKlicken auf einen Druckbutton:

PrintTree Me.acxTreeView.Nodes(1), 1, _
"SELECT * FROM Mitarbeitertabelle ORDER BY ElementNr"
Sub PrintTree(objNode As MSComctlLib.Node, _
              intIndentLevel As Integer, strSource As String)
'Gibt den übergebenen Node und seine Töchter in die Tabelle
'tblErgebnisse_Baumform (Export) aus. Dabei werden die
'Ebenen durch jeweils 5 Leerzeichen "simuliert", um den
'Baumefekt zu erreichen (Treeviews lassen sich nicht drucken).
 
'Der übergebene Node wird ausgegeben
CurrentDb.Execute "INSERT INTO [TreeView_Drucktabelle] " _
                & "(Daten) " _
                & "VALUES " _
                & "('" & Space((intIndentLevel - 1) * 5) & objNode.Text & "')"
 
'Wenn Node Töchter hat...
If objNode.Children > 0 Then
  'nächsten Level ermitteln (Push)
  intIndentLevel = intIndentLevel + 1
  'Aufruf der Routine für den ersten Tochternode (Rekursion)
  PrintTree objNode.Child, intIndentLevel, strSource
End If
 
If intIndentLevel <> 0 Then
  'Nächsten Node anspringen
  Set objNode = objNode.Next
 
  'Wenn es einen nächsten Node auf gleicher
  'Ebene gibt, diesen ebenso behandeln
  If TypeName(objNode) <> "Nothing" Then
    PrintTree objNode, intIndentLevel, strSource
  Else
    'Wenn keine Nodes auf gleicher Ebene mehr
    'vorhanden sind, Suchebene um 1 reduzieren (Pop)
    intIndentLevel = intIndentLevel - 1
  End If
End If
End Sub

Siehe auch