Alle Datenbanken einlesen und anzeigen

Für den einen oder anderen Anwendungsfall benötigen Sie einen Datei-öffnen-Dialog, um eine Datenbank auszuwählen. Dieser Dialog hat den Nachteil, dass er selten direkt die gewünschte Datenbank geschweige denn das Verzeichnis anzeigt. Wie wäre es also mit einem speziellen Dialog, der nur die Verzeichnisse einliest, die überhaupt Datenbankdateien enthalten, und diese in einem TreeView-Steuerelement zur Auswahl bereithält

Ziel

Das Ziel dieses Beitrags ist es, einen Dialog zu erstellen, der alle Datenbankdateien oder auch nur die in einem bestimmten Unterordner befindlichen anzeigt und zur Auswahl anbietet. Damit sollen die üblichen Dialoge ersetzt werden, die erstens immer alle Verzeichnisse und Dateien anzeigen und zweitens nie das Verzeichnis liefern, das man gerade benötigt. Für die Erstellung eines solchen Dialogs ist eine Menge Vorarbeit nötig – zum Beispiel müssen Sie eine Prozedur programmieren, die alle Verzeichnisse ab dem gewünschten Unterordner erfasst und nach Datenbankdateien durchsucht. Diese werden über die Dateiendung erkannt (.mdb, .accdb …). Der Dialog soll nur diejenigen Verzeichnisse anzeigen, die direkt oder in einem Unterverzeichnis mindestens eine Datenbankdatei enthalten. Bild 1 zeigt, wie der Dialog ungefähr aussehen soll. Sie erkennen hier ein Textfeld, in das Sie das beim Einlesen zu verwendende Startverzeichnis eintragen. Dieses lässt sich mit einem Mausklick auf die Schaltfläche rechts daneben auch per Verzeichnis auswählen-Dialog einlesen. Ein Klick auf die Schaltfläche Einlesen startet den Einlesevorgang, der je nach Umfang der einzulesenden Daten eine Weile dauern kann (auf meiner Festplatte, die zugegebenermaßen sehr viele Daten enthält, etwa acht Minuten – planen Sie also eine Kaffeepause für diesen Vorgang ein). Wenn Sie einmal ermittelt haben, wo sich die Datenbankdateien befinden, können Sie für den nächsten Einlesevorgang gegebenenfalls einen entsprechenden Unterordner als Startverzeichnis angeben, um Zeit zu sparen. Zu Beginn sollten Sie jedoch einmal die komplette Festplatte einlesen – ich fand es jedenfalls sehr interessant, an welchen Stellen überall Datenbankdateien gespeichert sind.

pic003.png

Bild 1: Dialog zur Anzeige von Datenbankdateien

Die erfassten Daten werden in einer Tabelle gespeichert, die wir weiter unten vorstellen. Die Daten sollen dann in einem TreeView-Steuerelement angezeigt werden. Dazu liest die Anwendung die Daten der Tabelle aus und trägt sie in das TreeView-Steuerelement ein. Verzeichnisse und Datenbankdateien werden mit entsprechenden Symbolen gekennzeichnet. Vorerst soll das Formular lediglich beim Schließen den Pfad der aktuell ausgewählten Datenbankdatei zurückliefern.

Überlegungen

Schauen wir uns die Ausgangssituation an. Das Dateisystem enthält beliebig tief verschachtelte Verzeichnisstrukturen, von denen nicht alle Access-Dateien speichern. Das zu füllende TreeView-Steuerelement soll lediglich die Verzeichnisse anzeigen, in denen sich auch Access-Dateien befinden. Dazu müssen wir alle Verzeichnisse durchlaufen und diejenigen mit Access-Dateien ermitteln. Die Pfade der Verzeichnisse mit Access-Dateien sollen dann vom Laufwerksbuchstaben angefangen über die einzelnen Verzeichnisse im TreeView-Steuerelement abgebildet werden.

Wie erledigen wir dies am einfachsten, wie werden die Daten über die Verzeichnisse und Access-Datenbanken gespeichert, wie füllen wir das TreeView-Steuerelement und wie oft soll diese Prozedur wiederholt werden

Verzeichnisse und Dateien per FileSystemObject durchlaufen

Die Werkzeuge zum Einlesen der Verzeichnisse und Dateien liefert das FileSystemObject. Es bietet Möglichkeiten, die Verzeichnisse rekursiv zu durchlaufen und die Dateien auf ihre Dateiendung hin zu untersuchen.

Informationen speichern

Die Tabelle zum Aufnehmen der Informationen muss folgende Felder enthalten und sieht, mit einigen Daten gefüllt, wie in Bild 2 aus:

pic001.png

Bild 2: Tabelle zum Speichern der Dateien und Verzeichnisse

  • DateiVerzeichnisID: Primärschlüsselfeld der Tabelle, allerdings nicht als Autowert ausgelegt – mehr dazu später
  • DateiVerzeichnisname: Name der Datei oder des Verzeichnisses
  • IstVerzeichnis: Ja/Nein-Feld, das angibt, ob es sich bei dem Eintrag um ein Verzeichnis handelt
  • VerzeichnisID: Wert des Feldes DateiVerzeichnisID für das übergeordnete Verzeichnis
  • Pfad: Speichert den kompletten Pfad des aktuellen Datensatzes.
  • Expanded: Für die Darstellung im TreeView fügen Sie der Tabelle außerdem noch ein Feld zum Speichern des Expanded-Zustands hinzu.
  • Enthaltene Dateien: Speichert die Anzahl der im aktuellen Verzeichnis samt Unterverzeichnis enthaltenen Datenbankdateien.

Dateien einlesen und speichern

Warum verwenden wir in der Tabelle keinen Autowert für das Primärschlüsselfeld Normalerweise würden wir so vorgehen: Wir lesen das Root-Element ein, also beispielsweise das Verzeichnis c:\ und speichern es in der Tabelle. Dann arbeiten wir uns durch alle untergeordneten Verzeichnisse. Wenn c:\ nun mit dem Wert 1 im Primärschlüsselfeld gespeichert wurde, würden wir für die untergeordneten Verzeichnisse beziehungsweise Einträge den Wert 1 im Feld VerzeichnisID eintragen.

Dazu müssen wir aber jedes Verzeichnis in der Tabelle anlegen – auch wenn wir feststellen, dass sich weder in diesem Verzeichnis noch in einem der Unterverzeichnisse eine Access-Datei befindet. Und genau dies wollen wir ja verhindern, um nur die notwendigsten Verzeichnisse und Dateien in der Tabelle zu speichern.

Zur Veranschaulichung liefert Bild 3 einige Verzeichnisse und Dateien. Die Beschriftungen rechts neben den Elementen enthalten die Werte für das Primärschlüsselfeld DateiVerzeichnisID (in der Abbildung id) und für das Fremdschlüsselfeld VerzeichnisID (in der Abbildung fid).

pic002.png

Bild 3: Indizierung der Verzeichnisse und Dateien

Das Speichern des ersten Elements c:\ ist kein Problem – dieses soll auf jeden Fall gespeichert werden. Es erhält als Primärschlüsselwert etwa den Wert 1. Das Fremdschlüsselfeld bleibt leer, weil es ja keine übergeordneten Verzeichnisse gibt.

Das Unterverzeichnis Verzeichnis 1 würde mit dem Primärschlüsselwert 2 gespeichert. Das Feld VerzeichnisID erhält den Primärschlüsselwert des übergeordneten Verzeichnisses, also 1. Das Unterverzeichnis Verzeichnis 1-1 erhält den Primärschlüsselwert 3 und wird über den Wert 2 für das Feld VerzeichnisID dem Verzeichnis mit dem Primärschlüsselwert 2 untergeordnet.

Nun folgt die erste Datei, die nach dem gängigen Schema den Wert 4 im Feld DateiVerzeichnisID und den Wert 3 im Fremdschlüsselfeld VerzeichnisID erhält.

Wenn alle Verzeichnisse am Ende mindestens eine Datenbankdatei im letzten Verzeichnis enthielten, könnte man die Dateien auf diese Weise anlegen und durchlaufen. Es müssen jeweils die übergeordneten Verzeichnisse zuerst angelegt werden, damit den darunter befindlichen Elementen die Primärschlüsselwerte als Fremdschlüsselwert zugeordnet werden können.

Nun gibt es aber Verzeichnisse wie Verzeichnis1-2, das leer ist oder nur Dateien enthält, die nicht in die Tabelle aufgenommen werden sollen. Wir würden es mit den Werten 5 im Feld DateiVerzeichnisID und 2 im Feld VerzeichnisID speichern. Erst danach würden wir die in diesem Verzeichnis enthaltenen Elemente durchlaufen und schließlich feststellen, dass dort keine Datenbankdatei zu finden ist.

In diesem Fall gibt es kein weiteres untergeordnetes Verzeichnis, sodass die Untersuchung schnell abgeschlossen ist. Was aber nun mit dem Datensatz für das Verzeichnis mit dem Namen Verzeichnis 1-2 Der Datensatz kann wieder gelöscht werden, da er ja nicht mehr benötigt wird. Dummerweise gibt es gelegentlich sehr tiefe Verzeichnisstrukturen, in denen sich keine Datenbankdateien finden lassen. Dort würde man erst einige Datensätze für die Verzeichnisstruktur anlegen und müsste diese anschließend wieder löschen.

Dies ist der Grund, warum wir keinen Autowert als Primärschlüsselwert verwenden: Damit die Datensätze für Verzeichnisse ohne untergeordnete Datenbanken gar nicht erst angelegt werden, soll die entsprechende Funktion sich erst rekursiv bis zum Ende der Verzeichnisstruktur bewegen und prüfen, ob dort Datenbankdateien vorliegen. Ist dies der Fall, legt die Funktion zunächst das Element für die Datenbankdatei an und dann die darüber liegenden Verzeichnisse. Sprich: Im Falle von Datei 1 würde erst ein Datensatz für die Datei, dann jeweils einer für Verzeichnis 1-1 und für Verzeichnis 1 angelegt werden.

Dummerweise wissen wir beim Anlegen der untergeordneten Elemente noch nicht, welchen Primärschlüsselwert dessen Verzeichnis in der Tabelle aufweist. Wir müssen beim Navigieren in Richtung untergeordneter Verzeichnisse einen Primärschlüsselwert vorhalten und diesen dann von unten nach oben als Fremdschlüsselwert des jeweils untergeordneten Elements einfügen.

Wenn dabei wie im Fall von Verzeichnis 1-2 zunächst ein Primärschlüsselwert wie etwa 5 festgelegt wird, sich aber herausstellt, dass dieses Verzeichnis keine Access-Datenbanken enthält und somit nicht gespeichert werden soll, kann der Primärschlüsselwert 5 für das nachfolgend untersuchte Verzeichnis reserviert werden.

Rekursive Einlesefunktion

Wie lässt sich dies per VBA umsetzen Ganz einfach – mit einer rekursiv definierten Funktion. Diese besteht im vorliegenden Fall aus einer initialisierenden Funktion, die einen oder mehrere Datensätze für das angegebene Root-Verzeichnis schreibt und dann die eigentliche rekursive Funktion aufruft.

Schauen wir uns zunächst die Parameter der Startprozedur DateienEinlesen an (s. Listing 1):

Listing 1: Prozedur zum Initiieren des rekursiven Dateieinlese-Vorgangs

Public Sub DateienEinlesen(Optional strPfad As String, Optional intTiefeMax As Integer)
    Dim objFSO As Scripting.FileSystemObject
    Dim db As DAO.Database
    Dim strVerzeichnisse() As String
    Dim i As Long
    Set objFSO = New Scripting.FileSystemObject
    Set db = CurrentDb
    If Len(strPfad) = 0 Then
         strPfad = "c:\"
    End If
    strPfad = objFSO.GetFolder(strPfad)
    If Right(strPfad, 1) = "\" Then
         strPfad = Left(strPfad, Len(strPfad) - 1)
    End If
    db.Execute "SELECT * INTO tblDateienVerzeichnisse_" & Format(Now, "yyyymmdd_hhnnss") _
        & " FROM tblDateienVerzeichnisse", dbFailOnError
    db.Execute "DELETE FROM tblDateienVerzeichnisse", dbFailOnError
    If objFSO.FolderExists(strPfad) Then
         strVerzeichnisse = Split(strPfad, "\")
        For i = LBound(strVerzeichnisse) To UBound(strVerzeichnisse)
            If i = 0 Then
                db.Execute "INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID,
                DateiVerzeichnisName, IstVerzeichnis) VALUES(" & i + 1 & ", ''" _
                    & strVerzeichnisse(i) & "'', -1)", dbFailOnError
            Else
                db.Execute "INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID,
                DateiVerzeichnisName, IstVerzeichnis, VerzeichnisID) VALUES(" & i + 1 & ", ''" _
                    & strVerzeichnisse(i) & "'', -1, " & i & ")", dbFailOnError
            End If
        Next i
    End If
    lngAktuellID = i + 1
    DateienEinlesenRek db, objFSO, objFSO.GetFolder(strPfad & "\"), i, 0, intTiefeMax, 0
End Sub
  • strPfad: Enthält die Angabe des Startverzeichnisses für den Einlesevorgang. Wenn Sie den Parameter weglassen, liest die Prozedur das komplette Verzeichnis c:\ ein.
  • intTiefeMax: Dies ist eher ein Parameter, der für Entwicklungstests verwendet wurde. Damit können Sie angeben, wie tief die Funktion nach Access-Dateien suchen soll. Wenn Sie den Parameter weglassen, durchsucht die Funktion alle Verzeichnisebenen.

Die Prozedur deklariert zunächst ein Objekt auf Basis von Scripting.FileSystemObject. Dieses liefert die Methoden und Eigenschaften, um auf das Dateisystem zuzugreifen. Außerdem benötigen wir eine Database-Variable, um per Execute-Methode SQL-Aktionsabfragen ausführen zu können.

Die Prozedur prüft zunächst, ob der Parameter strPfad übergeben wurde und ersetzt diesen gegebenenfalls durch das Verzeichnis c:\. Der folgende Aufruf der GetFolder-Funktion des FileSystemObjects entledigt den Inhalt von strPfad eventuell anhängender Backslash-Zeichen, aus c:\Daten\ wird also c:\Daten.

Die folgende Anweisung kopiert den kompletten Inhalt der Tabelle tblDateienVerzeichnisse in eine neue Tabelle, die den gleichen Namen erhält, jedoch um Datum und Uhrzeit ergänzt. Dieses Feature war während der Entwicklung hilfreich, um umfangreiche erfolgreiche Einlesevorgänge zu sichern, bevor der Code weiter verfeinert wurde.

Danach leert die Tabelle tblDateienVerzeichnisse, da diese ja nun neu gefüllt werden soll. Ist das in strPfad angegebene Verzeichnis vorhanden, beginnt die eigentliche Bearbeitung.

Wenn der Benutzer einen Pfad angibt, der aus mehr als nur dem Laufwerksbuchstaben besteht, soll die Prozedur nämlich für jedes enthaltene Verzeichnis gleich einen Eintrag zur Tabelle tblDateienVerzeichnisse hinzufügen – und zwar verschachtelt. Dazu überträgt die Prozedur zunächst mit der Split-Funktion alle Verzeichnisse des Pfades in ein Array namens strVerzeichnisse().

Die folgende For…Next-Schleife durchläuft dazu alle Elemente dieses Arrays und legt jeweils einen neuen Eintrag in der Tabelle tblDateienVerzeichnisse an. Dabei gibt es wiederum zwei Möglichkeiten: Hat die Laufvariable i den Wert 0, wird das erste Element mit einer SQL-Anweisung wie folgt in die Tabelle tblDateienVerzeichnisse eingetragen:

INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID, DateiVerzeichnisName, IstVerzeichnis) VALUES(1, ''C:'', -1)

Das heißt, dass das Feld VerzeichnisID zum Eintragen der Referenz auf das übergeordnete Verzeichnis leer bleibt. Für die übrigen Einträge gibt die Prozedur jeweils das übergeordnete Verzeichnis als weiteren Parameter der INSERT INTO-Anweisung an:

INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID, DateiVerzeichnisName, IstVerzeichnis, VerzeichnisID) VALUES(2, ''Daten'', -1, 1)

In diesem Fall referenziert das hier angelegte zweite Element mit dem Primärschlüsselwert 2 das erste Element mit dem Primärschlüsselwert 1 über das Feld VerzeichnisID mit dem Wert 1. Wenn Sie mit dem Parameter strPfad also beispielsweise den Pfad c:\Daten\Fachartikel\AccessImUnternehmen\2013\03\ übergeben, sieht der Inhalt der Tabelle tblDateienVerzeichnisse nach dem Durchlaufen der For…Next-Schleife wie in Bild 4 aus.

pic004.png

Bild 4: Datensätze für den als Parameter übergebenen Pfad

Damit ist der erste Teil erledigt – alle Verzeichnisse des übergebenen Pfades wurden in der Tabelle angelegt.

Nun geht es mit der eigentlichen rekursiven Funktion weiter, die alle unterhalb dieses Pfades befindlichen Verzeichnisse und Dateien untersucht. Vorher stellt die Prozedur jedoch noch die wie folgt global deklarierte Variable lngAktuellID auf den Wert der Laufvariablen i ein:

Dim lngAktuellID As Long

lngAktuellID speichert den jeweils zu verwendenden Primärschlüsselwert beim Anlegen eines neuen Datensatzes.

Rekursive Funktion

Die rekursiv definierte Funktion DateienEinlesenRek erwartet die folgenden Parameter:

  • db: Verweis auf das Database-Objekt für die aktuelle Datenbank
  • objFSO: Verweis auf das FileSystemObject
  • objVerzeichnis: Verzeichnisse, deren enthaltene Unterverzeichnisse und Dateien in diesem Aufruf untersucht werden sollen
  • lngVerzeichnisID: Wert des Feldes DateiVerzeichnisID des übergeordneten Verzeichnisses in der Tabelle tblDateienVerzeichnisse
  • intTiefe: Aktuelle Verzeichnistiefe
  • intTiefeMax: Maximal zu untersuchende Verzeichnistiefe
  • lngAnzahl: Rückgabeparameter mit der Anzahl der im zu untersuchenden Unterverzeichnis enthaltenen Datenbankdateien

Die Funktion DateienEinlesenRek stellt zunächst die als Parameter übergebene Variable lngAnzahl auf den Wert 0 ein (s. Listing 2). Diese Variable soll, sofern das aktuelle Verzeichnis Datenbankdateien enthält, entsprechend angepasst werden. Außerdem werden noch die in den unterhalb des aktuellen Verzeichnisses befindlichen Datenbankdateien hinzugezählt.

Listing 2: Rekursiver Teil der Routinen zum Einlesen der Verzeichnisstruktur und der enthaltenen Dateien

Public Function DateienEinlesenRek(db As DAO.Database, objFSO As Scripting.FileSystemObject, _
    objVerzeichnis As Scripting.Folder, lngVerzeichnisID As Long, intTiefe As Integer, _
    intTiefeMax As Integer, lngAnzahl As Long) As Boolean
    Dim objUnterverzeichnis As Scripting.Folder
    Dim objDatei As Scripting.File
    Dim lngDateiVerzeichnisID As Long
    Dim bolDiesesVerzeichnisSpeichern As Boolean
    Dim bolVaterverzeichnisSpeichern As Boolean
    Dim lngAnzahlRek As Long
    lngAnzahl = 0
    If (intTiefeMax > 0) And (intTiefe > intTiefeMax) Then
        Exit Function
    End If
    On Error Resume Next
    For Each objDatei In objVerzeichnis.Files
        Select Case objFSO.GetExtensionName(objDatei.Name)
            Case "mdb", "accdb", "mda", "accda", "mde", "accde"
                lngAnzahl = lngAnzahl + 1
                db.Execute "INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID, " _
                    & "DateiVerzeichnisName, IstVerzeichnis, VerzeichnisID, Pfad) VALUES(" _
                    & lngAktuellID & ", ''" & objDatei.Name & "'', 0, " & lngVerzeichnisID & ", ''" _
                    & objDatei.Path & "'')", dbFailOnError
                lngAktuellID = lngAktuellID + 1
                DateienEinlesenRek = True
        End Select
    Next objDatei
    bolVaterverzeichnisSpeichern = False
    For Each objUnterverzeichnis In objVerzeichnis.SubFolders
        lngDateiVerzeichnisID = lngAktuellID
        lngAktuellID = lngAktuellID + 1
        bolDiesesVerzeichnisSpeichern = DateienEinlesenRek(db, objFSO, objUnterverzeichnis, _
        lngDateiVerzeichnisID, intTiefe + 1, intTiefeMax, lngAnzahlRek)
        lngAnzahl = lngAnzahl + lngAnzahlRek
        bolVaterverzeichnisSpeichern = bolVaterverzeichnisSpeichern Or bolDiesesVerzeichnisSpeichern
        If bolDiesesVerzeichnisSpeichern = True Then
            db.Execute "INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID, " _
                & "DateiVerzeichnisName, IstVerzeichnis, VerzeichnisID, EnthalteneDateien) VALUES(" _
                & lngDateiVerzeichnisID & ", ''" & objUnterverzeichnis.Name & "'', -1, " _
                & lngVerzeichnisID & ", " & lngAnzahlRek & ")", dbFailOnError
        End If
    Next objUnterverzeichnis
    DateienEinlesenRek = DateienEinlesenRek Or bolVaterverzeichnisSpeichern
End Function

Dann prüft sie, ob der Benutzer überhaupt einen Wert für den Parameter intTiefeMax angegeben hat und somit eine maximal zu durchsuchende Verzeichnistiefe. Falls ja, vergleicht sie diesen Wert mit der in intTiefe gespeicherten aktuellen Verzeichnistiefe.

Sollte die maximale Tiefe erreicht worden sein, bricht die Funktion sich selbst ab. Danach untersucht die Funktion zunächst alle Dateien des aktuellen Verzeichnisses, und zwar in einer For Each-Schleife über alle Objekte der Files-Auflistung des mit objVerzeichnis referenzierten aktuellen Verzeichnisses.

Die folgende Select Case-Bedingung prüft den Wert der mit der Funktion GetExtensionName ermittelten Dateiendung. Hat diese den Wert mdb, accdb, mda, accda, mde oder accde, handelt es sich um eine Datenbankdatei.

Sie können an dieser Stelle weitere Dateiendungen hinzufügen oder diese komplett ändern, um beispielsweise alle Word-Dokumente eines Verzeichnisses zu ermitteln. Wurde eine der angegebenen Dateiendungen gefunden, erhöht die Funktion den Wert von lngAnzahl um eins. Dann trägt sie die gefundene Datenbankdatei in die Tabelle tblDateienVerzeichnisse ein, und zwar beispielsweise mit folgender SQL-Anweisung:

INSERT INTO tblDateienVerzeichnisse(DateiVerzeichnisID, DateiVerzeichnisName, IstVerzeichnis, VerzeichnisID, Pfad)
VALUES(12, ''ABCAnalyse.mdb'', 0, 11, ''C:\Daten\...\ABCAnalyse.mdb'')

Danach erhöht die Funktion den Wert von lngAktuellID um eins. Diese Variable hat eine wichtige Aufgabe: Sie liefert den Wert für den jeweils neu anzulegenden Datensatz in der Tabelle tblDateienVerzeichnisse – sowohl beim Anlegen von Dateien als auch bei Verzeichnissen. Weiter oben haben wir ja bereits darauf hingewiesen, dass die Tabelle tblDateienVerzeichnisse keinen Autowert für das Primärschlüsselfeld verwendet, daher muss dieser Wert von der Funktion DateienEinlesenRek gepflegt werden.

Um lngAktuellID nicht immer als Parameter von Aufruf zu Aufruf weiterreichen zu müssen, haben wir diese Variable extern als modulweit gültige Variable deklariert. Sie läuft also quasi neben den Funktionsaufrufen her und wird darin jeweils erhöht, um immer einen eindeutigen Primärschlüsselwert bereitzustellen.

Wenn die Funktion in diesem Durchlauf mindestens eine Datenbankdatei gefunden hat, stellt sie den Wert des Rückgabewertes DateienEinlesenRek auf True ein. Dieser Wert bleibt auch erhalten, bis alle Dateien und Unterverzeichnisse des aktuellen Verzeichnisses durchlaufen wurden, und wird höchstens mehrfalls auf True eingestellt.

Nachdem alle eventuell vorhandenen Dateien durchlaufen und auf Datenbankdatei-Endungen geprüft wurden, durchläuft die Funktion die enthaltenen Unterverzeichnisse – ebenfalls in einer For Each-Schleife. Zuvor stellt die Prozedur noch eine Variable namens bolVaterverzeichnisSpeichern auf False ein – dazu später mehr.

Für jedes gefundene Verzeichnis speichert die Funktion zunächst den aktuellen Wert von lngAktuellID, unserer Primärschlüsselwert-Quelle, in der Variablen lngDateiVerzeichnisID und erhöht lngAktuellID wiederum um eins.

Bevor das aktuell durchlaufene Verzeichnis gespeichert wird, ruft sich die Funktion zunächst erneut selbst auf, um die in diesem Verzeichnis enthaltenen Unterverzeichnisse zu untersuchen. Warum das Nun: Wenn Sie das Verzeichnis vorher schon speichern, ohne dass dieses eine Datenbankdatei enthält und ohne die Unterverzeichnisse zu durchsuchen, kann es sein, dass dieses Verzeichnis später wieder gelöscht werden muss. In unserer Variante des Speicherns der Verzeichnisstruktur durchlaufen wir zunächst alle Unterverzeichnisebenen und speichern Verzeichnisse erst, wenn mindestens eine Datenbankdatei in einem der Unterverzeichnisse enthalten ist.

Das ist ja der Trick: Die Lösung soll nur solche Verzeichnisse liefern, die auch Datenbankdateien enthalten. Alles andere würde nur die Übersicht verschlechtern.

Zurück zur Funktion: Diese ruft sich wie erwähnt erneut selbst auf, und zwar so lange, bis keine weiteren Unterverzeichnisse mehr gefunden werden oder bis die maximal zu durchsuchende Verzeichnistiefe erreicht ist. Wenn die Funktion irgendwann während dieses Vorgangs eine Datenbankdatei gefunden hat, liefert sie als Funktionswert den Wert True zurück – dies geschieht ja bereits weiter oben beim Durchlaufen der im aktuellen Verzeichnis enthaltenen Dateien.

Aber nicht nur das: Beim Aufruf der Funktion DateienEinlesenRek liefert der Rückgabeparameter lngAnzahlRek den in diesem Aufruf ermittelten Wert der Variablen lngAnzahl, also die Anzahl der im Verzeichnis gefundenen Datenbankdateien. Dieser durch die Funktion gefüllte Wert wird zum aktuellen Wert der Variablen lngAnzahl hinzuaddiert. Auf diese Weise wird die Anzahl der Datenbankdateien von den untersten Verzeichnisebenen nach oben hin aufkumuliert, sodass für jedes Verzeichnis die insgesamt in allen Unterverzeichnissen enthaltene Anzahl Datenbankdateien gespeichert werden kann.

Eine weitere Variable namens bolVaterverzeichnisSpeichern legt fest, ob das übergeordnete Verzeichnis entweder wegen einer in diesem Verzeichnis liegenden Datenbankdatei oder wegen einer in den darunter liegenden Verzeichnissen enthaltenen Datenbankdatei gespeichert werden soll. bolVaterverzeichnisSpeichern wird beim ersten Auftauchen eines Verzeichnisses mit Datenbankdatei auf True eingestellt und Ebene für Ebene in der Hierarchie der Funktionsaufrufe nach oben durchgereicht.

Hat bolDiesesVerzeichnisSpeichern den Wert True, legt die Prozedur in der Tabelle tblDateienVerzeichnisse einen Datensatz für das aktuelle Verzeichnis an.

Das bedeutet, dass bei Durchlaufen eines Pfades wie c:\Verzeichnis1\Verzeichnis2\Verzeichnis3 und dem Auffinden einer Datenbankdatei in Verzeichnis 3 die rekursive Funktion zunächst in das Verzeichnis c:\Verzeichnis1\Verzeichnis2\Verzeichnis3 abtaucht und dieses speichert und erst dann c:\Verzeichnis1\Verzeichnis2 und c:\Verzeichnis1.

Schließlich gibt die Prozedur als Funktionswert den Wert True zurück, wenn in diesem oder einen weiteren rekursiven Aufruf mindestens eine Datenbankdatei gefunden werden konnte.

Nach dem Aufruf und dem Abarbeiten der rekursiven Funktion ist die Tabelle tblDateienVerzeichnisse mit den gewünschten Daten gefüllt und kann für die Anzeige im TreeView-Steuerelement weiterverwendet werden.

Formular erstellen

Nach den etwas umfangreicheren Vorbereitungen erstellen wir nun das Formular zur Anzeige und Auswahl von Datenbankdateien. Das Formular soll frmDateienVerzeichnisse heißen. Im Kopf des Formulars legen wir ein Textfeld namens txtStartverzeichnis zur Eingabe/Auswahl des Startverzeichnisses beim Einlesen der Verzeichnisse an. Rechts daneben platzieren wir eine Schaltfläche, die einen Verzeichnisauswahl-Dialog einblendet und die getroffene Auswahl in das Textfeld txtStartverzeichnis überträgt.

Diese Schaltfläche verwendet die folgende Prozedur, um eine im Modul mdlTools befindliche Funktion zum Ermitteln eines Verzeichnisses aufzurufen:

Private Sub cmdVerzeichnisWaehlen_Click()
    Me!txtStartverzeichnis = OpenPathName(CurrentProject.Path, "Startverzeichnis auswählen")
End Sub

Darunter platzieren wir zwei Schaltflächen namens cmdEinlesen und cmdBaumFuellen. Die Schaltfläche cmdEinlesen startet die Prozedur DateienEinlesen mit dem im Textfeld angegebenen Verzeichnis:

Private Sub cmdEinlesen_Click()
    DateienEinlesen Nz(Me!txtStartverzeichnis)
End Sub

Die Schaltfläche cmdBaumFuellen ruft die noch zu erstellende Prozedur BaumFuellen auf:

Private Sub cmdBaumFuellen_Click()
    BaumFuellen
End Sub

Da das Formular nicht an eine Datenherkunft gebunden ist, können Sie Eigenschaften wie Navigationsschaltflächen, Datensatzmarkierer und Bildlaufleisten auf Nein einstellen.

Dateien im TreeView-Steuerelement

Nun soll das TreeView-Steuerelement mit den Daten der Tabelle tblDateienVerzeichnisse gefüllt werden. Das TreeView-Steuerelement soll ein paar aussagekräftige Icons anzeigen, es soll sich merken, welche Einträge beim vorherigen Bearbeiten ein- und welche ausgeklappt waren, und es soll folgende Kontextmenüeinträge für die Datenbankeinträge bereitstellen (s. Bild 6):

pic006.png

Bild 5: Diese Abfrage liefert das Root-Element für das TreeView-Steuerelement

pic005.png

Bild 6: Kontextmenü eines Datenbankeintrags

  • Zu Verzeichnis wechseln: Öffnet das Verzeichnis, in dem sich die Datenbank befindet.
  • Datenbank öffnen: Öffnet Access mit der angegebenen Datenbank.
  • Löschen: Löscht die Datenbank nach vorheriger Rückfrage.

Außerdem soll das Textfeld txtDatei im unteren Bereich nach einem Klick auf einen der TreeView-Einträge mit dem Namen der jeweiligen Datenbankdatei gefüllt werden.

Klickt der Benutzer auf ein Verzeichnis, soll das Textfeld geleert werden.

Dies sind bereits einige Anforderungen an ein TreeView-Steuerelement. Grund genug, den TreeViewHandler, den wir zuletzt im Beitrag TreeView-Konfigurator erweitert (www.access-im-unternehmen.de/771) vorgestellt haben, einzusetzen.

Mit diesem lässt sich das TreeView leicht konfigurieren und füllen. Außerdem bringt der TreeViewHandler einige Funktionen wie das Speichern der Expanded-Eigenschaft mit, die Sie sonst selbst programmieren müssten.

TreeViewHandler konfigurieren

Alle für den TreeViewHandler benötigten Komponenten sind bereits in der Beispieldatenbank AlleDatenbankenEinlesen.mdb enthalten. Um das Steuerelement zu konfigurieren, öffnen Sie das Formular frmTreeViewConf der Beispieldatenbank.

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