Dateien schnell im TreeView-Steuerelement anzeigen

Im Artikel „Ordner und Dateien in Access-Tabellen einlesen“ (www.access-im-unternehmen.de/1583) haben wir gezeigt, wie wir den Inhalt kompletter Ordner samt Unterordnern und Dateien in Tabellen speichern. Doch was helfen die dort liegenden Daten, wenn wir sie nicht in einem Access-Formular anzeigen können? Wie das gelingt, zeigen wir im vorliegenden Artikel. Als Steuerelement für die Anzeige hierarchischer Daten ist das TreeView-Steuerelement prädestiniert. Wir möchten alle Elemente der Tabellen aus dem oben genannten Artikel in einem solchen Steuerelement anzeigen und weitere Funktionen hinzufügen: die Anzeige des jeweiligen Ordners direkt im Windows Explorer, das Öffnen der aktuell markierten Datei oder auch das Ausschneiden, Kopieren und Einfügen, das wir nicht nur auf die Elemente des TreeView-Steuerelements anwenden, sondern auch auf die Originaldateien. Auch das Umbenennen von Ordnern und Dateien soll möglich sein – und schließlich wollen wir auch noch deren Speicherort durch Drag and Drop anpassen können. In diesem Artikel erfährst Du erst einmal, wie Du das TreeView schnell mit Ordnern und Dateien füllen kannst.

Formular mit TreeView-Steuerelement erstellen

Zunächst legen wir ein neues Formular namens frmDateienImTreeview in der Entwurfsansicht an.

Diesem fügen wir gleich das TreeView-Steuerelement hinzu, mit dem wir die Dateien anzeigen wollen, und nennen es ctlTreeView.

Das TreeView-Steuerelement soll für seine Einträge Icons anzeigen, daher fügen wir noch ein ImageList-Steuerelement namens ctlImageList hinzu. Oben fügen wir weitere Steuerelemente ein:

  • ein Textfeld namens txtBasispfad zur Anzeige des aktuellen Hauptverzeichnisses,
  • eine Schaltfläche namens cmdOrdnerAuswaehlen zum Auswählen des Hauptverzeichnisses, dessen Unterordner und Dateien angezeigt werden sollen,
  • ein Kontrollkästchen namens chkDateienImTreeViewAnzeigen, um festzulegen, ob die Dateien im TreeView-Steuerelement ein- oder ausgeblendet werden sollen, und
  • eine weitere Schaltfläche namens cmdNeuEinlesen, mit der wir das TreeView-Steuerelement erneut füllen können.

Der Entwurf sieht nun zunächst wie in Bild 1 aus.

Grundaufbau des Formulars

Bild 1: Grundaufbau des Formulars

Optionentabelle anlegen

Die Einstellungen der beiden Steuerelemente txtBasispfad und chkDateienImTreeViewAnzeigen wollen wir speichern und jeweils beim nächsten Öffnen des Formulars wiederherstellen. Dazu benötigen wir eine Tabelle namens tblOptionen, deren Entwurf wie in Bild 2 aussieht.

Entwurf der Optionen-Tabelle

Bild 2: Entwurf der Optionen-Tabelle

Das Formular binden wir über die Eigenschaft Datensatzquelle an die Tabelle tblOptionen, die beiden Steuerelemente txtBasispfad und chkDateienImTreeViewAnzeigen über die Eigenschaft Steuerelementinhalt an die jeweiligen Felder der Optionentabelle.

Basispfad auswählen

Damit wir den Ordner auswählen können, dessen Unterordner und Dateien im TreeView-Steuerelement angezeigt werden sollen, hinterlegen wir für die Schaltfläche cmdOrdnerAuswaehlen die folgende Ereignisprozedur:

Private Sub cmdOrdnerAuswaehlen_Click()
     Me.txtBasispfad = ChooseFolder
     Me.Dirty = False
End Sub

Diese zeigt über die Funktion ChooseFolder einen Ordnerauswahl-Dialog an:

Public Function ChooseFolder()
     Dim objFileDialog As Office.FileDialog
     Dim strTemp As String
     Set objFileDialog = _
         Application.FileDialog(msoFileDialogFolderPicker)
     With objFileDialog
         .Title = "Datei auswählen"
         .ButtonName = "Auswählen"
         .InitialFilename = CurrentProject.Path & "\"
         If .Show = True Then
             strTemp = .SelectedItems(1)
         End If
     End With
     ChooseFolder = strTemp
End Function

Um die hier verwendete Klasse FileDialog zu nutzen, müssen wir noch einen Verweis auf die Bibliothek Microsoft Office 16.0 Object Library hinzufügen.

Nach der Auswahl werden die Daten des Formulars mit Me.Dirty = False gespeichert.

Füllen des TreeView-Steuerelements

Bevor wir uns die Prozeduren ansehen, mit denen wir die Ordner und Dateien aus den Tabellen tblFolders und tblFiles in das TreeView-Steuerelement laden, fügen wir im Kopf des Klassenmoduls des Formulars frmDateienImTreeView die folgenden Deklarationsanweisungen hinzu:

Dim objTreeview As MSComctlLib.TreeView
Dim objImageList As MSComctlLib.ImageList
Dim dicFolders As Scripting.Dictionary
Dim dicFiles As Scripting.Dictionary

Die ersten beiden benötigen wir, um die Steuerelemente ctlTreeView und ctlImageList zu referenzieren, die beiden übrigen als temporären Speicher für die anzuzeigenden Ordner und Dateien. Um diese Dictionary-Elemente nutzen zu können, benötigen wir einen weiteren Verweis, dieses Mal auf die Bibliothek Microsoft Scripting Runtime.

Die Ereignisprozedur Beim Laden

In der Prozedur, die durch das Ereignis Beim Laden des Formulars ausgelöst wird (siehe Listing 1), aktivieren wir zunächst die Sanduhr (die wir am Ende wieder deaktivieren) und weisen der Variablen objImageList die Eigenschaft Object des Steuerelements ctlImageList zu. Damit können wir per IntelliSense auf die spezifischen Eigenschaften dieses Steuerelements zugreifen.

Private Sub Form_Load()
    
     DoCmd.Hourglass True
     
     Set objImageList = Me.ctlImageList.Object
     
     objImageList.ImageHeight = 16
     objImageList.ImageWidth = 16
     Call ImageListFuellen
     
     Set objTreeview = Me.ctlTreeView.Object
     With objTreeview
         Set .ImageList = objImageList
         .Nodes.Clear
         .Appearance = ccFlat
         .BorderStyle = ccNone
         .HideSelection = False
         .LineStyle = tvwRootLines
         .Indentation = 250
         .Font.name = "Calibri"
         .Font.Size = 10
         .OLEDragMode = ccOLEDragAutomatic
         .OLEDropMode = ccOLEDropManual
     End With
     
     Call FillTreeView(Me.chkDateienInTreeviewAnzeigen)
     Call SelectCurrentNode
     
     DoCmd.Hourglass False
End Sub

Listing 1: Diese Prozedur wird beim Laden des Formulars ausgeführt.

Dann stellen wir seine Eigenschaften ImageHeight und ImageWidth jeweils auf 16 Pixel ein, um die Größe der anzuzeigenden Icons festzulegen. Schließlich rufen wir die Prozedur ImageListFuellen auf (siehe Listing 2). Diese geht davon aus, dass wir die Icons, die wir im TreeView-Steuerelement anzeigen wollen, in der Systemtabelle MSysResources gespeichert haben.

Private Sub ImageListFuellen()
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim objImageList As MSComctlLib.ImageList
     
     Set db = CurrentDb
     Set rst = db.OpenRecordset("SELECT * FROM MSysResources WHERE Extension = ''PNG''", dbOpenSnapshot)
     
     Set objImageList = Me.ctlImageList.Object
     
     Set ctlTreeView.Object.ImageList = Nothing
     
     objImageList.ListImages.Clear
     
     Do While Not rst.EOF
         Call amvAddIconToImageListFromResourcesByName(objImageList, rst!name)
         rst.MoveNext
     Loop
     
     Dim objListImage As MSComctlLib.ListImage
     For Each objListImage In objImageList.ListImages
         Debug.Print objListImage.Index, objListImage.Key
     Next objListImage
         
     rst.Close
     Set rst = Nothing
     Set db = Nothing
End Sub

Listing 2: Füllen des ImageList-Steuerelements mit den Bildern aus MSysResources

Das haben wir bereits erledigt (siehe Bild 3). Die Prozedur ImageListFuellen öffnet ein Recordset basierend auf der Tabelle MSysResources, gefiltert nach den Elementen des Typs png.

Icons für das TreeView-Steuerelement in der Tabelle MSysResources

Bild 3: Icons für das TreeView-Steuerelement in der Tabelle MSysResources

Dann referenziert sie das ImageList-Steuerelement mit der bereits im Modulkopf deklarierten Variablen objImageList. Anschließend wird die Eigenschaft ImageList des Steuerelements ctlTreeView geleert – dieses wird später erneut zugewiesen.

Dann leert die Prozedur das ImageList-Steuerelement mit der Clear-Methode der ListImages-Auflistung. Dann ruft die Prozedur eine weitere Prozedur namens amvAddIconToImageListFromResourcesByName auf.

Diese wollen wir hier nicht im Detail beschreiben – sie lädt das Element mit dem Namen aus rst!Name für den aktuellen Datensatz des Recordsets und fügt es zum ImageList-Steuerelement hinzu (siehe Modul MDL_AMV_Pictures). Auf diese Weise landen alle .png-Dateien aus der Tabelle MSysResources im ImageList-Steuerelement und können so im TreeView-Steuerelement verwendet werden.

Um dies nicht im ImageList-Steuerelement prüfen zu müssen, gibt die Prozedur den Index und die Namen aller Elemente einmal im Direktbereich des VBA-Editors aus – diesen Bereich können wir im produktiven Einsatz später entfernen. Das Ergebnis sieht in diesem Fall wie folgt aus:

  1            book
  2            folder
  3            new
  4            delete
  5            refresh

Danach schließt und leert die Prozedur alle Objektvariablen.

Zurück in der Prozedur Form_Load referenzieren wir nun das TreeView-Steuerelement aus Me.ctlTreeView.Object mit der Variablen objTreeView und stellen einige Eigenschaften ein. Als Erstes weisen wir diesem für die Eigenschaft ImageList den Inhalt von objImageList zu. So weiß das TreeView-Steuerelement, woher es seine Icons beziehen soll, die wir nachher den Elementen zuweisen.

Dann leert die Prozedur alle gegebenenfalls noch vorhandenen Elemente. Schließlich folgt die Einstellung weiterer Eigenschaften, die für das Aussehen des TreeView-Steuerelements verantwortlich sind:

  • Appearance erhält den Wert ccFlat, damit es nicht mit 3d-Effekt angezeigt wird,
  • mit BorderStyle (Wert: ccNone) blenden wir den Rahmen des Steuerelements aus (wir fügen diesen über die Eigenschaften des Steuerelements selbst über das Eigenschaftenblatt wieder hinzu),
  • mit HideSelection (False) legen wir fest, dass das aktivierte Element auch beim Fokusverlust zumindest grau hinterlegt wird,
  • mit LineStyle (tvwRootLines) legen wir die Art der Verbindungslinien zwischen den Elementen fest,
  • Indentation stellen wir auf 250 ein, damit die Elemente nicht so weit eingerückt werden wie im Standard,
  • mit Font.Name und Font.Size stellen wir Schriftart und -größe fest und
  • mit OLEDragMode (ccOLEDragAutomatic) und OLEDropMode (ccOLEDropManual) legen wir fest, dass wir das Verhalten bei Drag and Drop selbst programmieren wollen.

Schließlich rufen wir die Prozedur FillTreeView auf, die das eigentliche Füllen des TreeView-Steuerelements initialisiert.

Dieser übergeben wir als Parameter den Wert des Kontrollkästchens chkDateienInTreeviewAnzeigen, das angibt, ob nur Ordner oder auch Dateien im TreeView angezeigt werden sollen.

Anschließend markierten wir noch das Element, das zuletzt selektiert wurde (mehr dazu später).

Füllen des TreeView-Steuerelements mit FillTreeView

Den Start beim Füllen des TreeView-Steuerelements macht die Prozedur FillTreeView, der wir einen Boolean-Wert übergeben, der angibt, ob neben den Ordnern auch Dateien angezeigt werden sollen oder nicht (siehe Listing 3).

Private Sub FillTreeView(Optional bolFiles As Boolean = False)
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     
     Set dicFolders = New Scripting.Dictionary
     Set dicFiles = New Scripting.Dictionary
     
     Set db = CurrentDb
     Set rst = db.OpenRecordset("SELECT FolderID, Foldername, ParentID, Expanded FROM qryFoldersFolderUserData", _
         dbOpenSnapshot)
     Do While Not rst.EOF
         AddFolderToDictionary rst!ParentID, rst!FolderID, rst!FolderName, rst!Expanded
         rst.MoveNext
     Loop
     rst.Close
     
     If bolFiles = True Then
         Set rst = db.OpenRecordset("SELECT FileID, Filename, ParentID FROM tblFiles ORDER BY Filename", dbOpenSnapshot)
         Do While Not rst.EOF
             AddFileToDictionary rst!ParentID, rst!FileID, rst!FileName
             rst.MoveNext
         Loop
         rst.Close
     End If
     
     BuildTree Null, Nothing
     
End Sub

Listing 3: Füllen des TreeView-Steuerelements mit FillTreeView

Diese Prozedur und die dadurch aufgerufenen Routinen sind auf Performance getrimmt. Das heißt, wir durchlaufen die Datensätze der Tabellen tblFolders und tblFiles nicht wie sonst üblich rekursiv und fügen Ordner für Ordner und Datei für Datei zum TreeView-Steuerelement hinzu, sondern nutzen eine andere Vorgehensweise. Dazu füllen wir zunächst die beiden Variablen dicFolders und dicFiles, die wir bereits im Kopf des Moduls deklariert haben, mit neuen, leeren Dictionary-Objekten.

Ordner in Dictionary einlesen

Dann durchlaufen wir alle Datensätze der Tabelle tblFolder, aber wir nutzen dazu eine vorgeschaltete Abfrage namens qryFoldersFolderUserData (siehe Bild 4). Diese fasst die Daten der Tabelle tblFolders mit denen der Tabelle FolderUserData zusammen – hier legen wir etwa fest, ob ein Ordner ausgeklappt angezeigt werden soll. Außerdem sortiert die Abfrage direkt aufsteigend nach dem Feld Foldername, damit diese später in der korrekten Reihenfolge eingelesen werden.

Abfrage mit Ordnern und der Information, ob ein Ordner ausgeklappt angezeigt werden soll

Bild 4: Abfrage mit Ordnern und der Information, ob ein Ordner ausgeklappt angezeigt werden soll

Recordsets öffnen wir hier übrigens immer mit dem Parameter dbOpenSnapshot, da wir immer nur lesen zugreifen und dies schneller ist.

Wir durchlaufen alle Elemente des Recordsets und rufen für jedes die Prozedur AddFolderToDictionary auf (siehe Listing 4). Wir beabsichtigen nämlich nicht, die Elemente direkt zum TreeView-Steuerelement hinzuzufügen, sondern diese erst im Dictionary dicFolders zwischenspeichern.

Private Sub AddFolderToDictionary(varParentID As Variant, lngFolderID As Long, strFolderName As String, _
         bolExpanded As Boolean)
     Dim col As Collection
     If Not dicFolders.Exists(Nz(varParentID, 0)) Then
         Set col = New Collection
         dicFolders.Add Nz(varParentID, 0), col
     End If
     
     dicFolders(Nz(varParentID, 0)).Add Array(lngFolderID, strFolderName, bolExpanded)
End Sub

Listing 4: Speichern der Ordner und weiterer Daten im Dictionary dicFolders

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