Mit Ordnern und Dateien im TreeView arbeiten, Teil 2

Im ersten Teil dieses Beitrags haben wir die Grundlagen für die Arbeit mit Ordnern und Dateien im TreeView-Steuerelement gelegt: Ordner und Dateien im Explorer anzeigen und öffnen, das Ermitteln des Pfades über die Tabellen-IDs, Kontextmenüs für Ordner- und Datei-Elemente sowie die vollständige Implementierung der Ordner-Operationen Kopieren, Ausschneiden, Einfügen, Umbenennen und Löschen. Im vorliegenden zweiten Teil ergänzen wir die noch fehlenden Datei-Operationen und erörtern die Funktionen im Modul „mdlDateisystem“, welche die TreeView-Änderungen tatsächlich auf das Dateisystem übertragen.

Voraussetzung

Dieser Beitrag baut unmittelbar auf Teil 1 auf (www.access-im-unternehmen.de/1585). Alle dort beschriebenen Module, Funktionen und Formulare werden vorausgesetzt und hier nicht erneut erklärt.

Wir setzen auf dem dort beschriebenen Formular frmDateienImTreeView auf (siehe Bild 1).

Das TreeView-Steuerelement mit den Ordnern und Dateien

Bild 1: Das TreeView-Steuerelement mit den Ordnern und Dateien

Ergänzung im Formular: SelectedItem setzen

Im Vergleich zum Listing aus Teil 1 wurde die Ereignisprozedur ctlTreeView_MouseDown um eine zusätzliche Zuweisung ergänzt. Bislang wurde objCurrentNode nur dann geleert und das markierte Element abgewählt, wenn kein Node getroffen wurde. Nun wird außerdem in allen anderen Fällen – also wenn tatsächlich ein Node-Element angeklickt wurde – die Eigenschaft SelectedItem des TreeView-Steuerelements explizit auf dieses Element gesetzt:

Set objCurrentNode = ctlTreeView.Object.HitTest(x, y)
If objCurrentNode Is Nothing Then
    Me.ctlTreeView.Object.SelectedItem = Nothing
Else
    Me.ctlTreeView.Object.SelectedItem = objCurrentNode
End If

Das stellt sicher, dass beim Aufruf von Kontextmenü-Funktionen, die über Screen.ActiveControl auf das aktive Steuerelement zugreifen, das angeklickte Element stets korrekt als markiert gilt.

Datei einfügen – neuer Kontextmenü-Eintrag im Ordner-Menü

Das Kontextmenü für Ordner-Elemente wurde gegenüber dem in Teil 1 gezeigten Listing um einen weiteren Eintrag ergänzt.

Nach dem Eintrag Ordner umbenennen erscheint nun auch Datei einfügen, der die Funktion DateiEinfuegen ohne Parameter aufruft:

With cbr.Controls.Add(msoControlButton)
    .Caption = "Datei einfügen"
    .OnAction = "=DateiEinfuegen()"
    .FaceId = 2512
End With

Dieser Eintrag erlaubt es, eine zuvor kopierte oder ausgeschnittene Datei in den Zielordner einzufügen, ohne dass das Datei-Kontextmenü benötigt wird.

Da DateiEinfuegen keinen Key-Parameter erwartet, ermittelt die Funktion das Ziel-Element selbst über Screen.ActiveControl.Object.SelectedItem.

Das Kontextmenü für Dateien

Das Kontextmenü für Datei-Elemente wird in der Prozedur CreateCommandBarFile zusammengesetzt. Es enthält die fünf Einträge Datei kopieren, Datei ausschneiden, Datei umbenennen, Datei löschen und Datei mit Zielanwendung öffnen (siehe Bild 2).

Kontextmenü für Dateien

Bild 2: Kontextmenü für Dateien

Der Aufbau der Prozedur aus Listing 1 ist identisch mit dem Ordner-Kontextmenü aus Teil 1.

Private Sub CreateCommandBarFile()
    Dim strKey As String
    Dim cbr As Office.CommandBar
    strKey = objCurrentNode.Key
    On Error Resume Next
    CommandBars("Ordner").Delete
    On Error GoTo 0
    Set cbr = CommandBars.Add("Ordner", msoBarPopup, False, True)
    With cbr.Controls.Add(msoControlButton)
        .Caption = "Datei kopieren"
        .OnAction = "=DateiKopieren('" & strKey & "')"
        .FaceId = 1667
    End With
    With cbr.Controls.Add(msoControlButton)
        .Caption = "Datei ausschneiden"
        .OnAction = "=DateiAusschneiden('" & strKey & "')"
        .FaceId = 1667
    End With
    With cbr.Controls.Add(msoControlButton)
        .Caption = "Datei umbenennen"
        .OnAction = "=DateiUmbenennen()"
        .FaceId = 1667
    End With
    With cbr.Controls.Add(msoControlButton)
        .Caption = "Datei löschen"
        .OnAction = "=DateiLoeschen('" & strKey & "')"
        .FaceId = 1667
    End With
    With cbr.Controls.Add(msoControlButton)
        .Caption = "Datei mit Zielanwendung öffnen"
        .OnAction = "=DateiOeffnen('" & strKey & "')"
        .FaceId = 1667
    End With
    cbr.ShowPopup
End Sub

Listing 1: Prozedur zum Anzeigen des Kontextmenüs für Dateien

Der Key des angeklickten Nodes wird aus objCurrentNode.Key gelesen und an die jeweilige Funktion übergeben.

Alle fünf Funktionen liegen im Modul mdlKontextmenues.

Dateien kopieren und ausschneiden

Analog zu den bereits beschriebenen Ordner-Variablen strKeyOrdnerKopiert und strKeyOrdnerAusgeschnitten gibt es im Modulkopf von mdlKontextmenues zwei weitere öffentliche Variablen für Dateien:

Public strKeyDateiKopiert As String
Public strKeyDateiAusgeschnitten As String

Die zugehörigen Funktionen schreiben den Key in die jeweilige Variable und leeren die andere:

Public Function DateiKopieren(strKey As String)
    strKeyDateiKopiert = strKey
    strKeyDateiAusgeschnitten = ""
End Function
Public Function DateiAusschneiden(strKey As String)
    strKeyDateiAusgeschnitten = strKey
    strKeyDateiKopiert = ""
End Function

Das Vorgehen ist das gleiche, das wir in Teil 1 bereits für Ordner beschrieben haben.

Eine Datei umbenennen

Beim Umbenennen einer Datei gibt es einen kleinen Unterschied zu Ordnern. Die Funktion OrdnerUmbenennen greift direkt über die globale Variable objTreeView auf das Steuerelement zu.

Für Dateien wurde eine andere Lösung gewählt, weil der Umbenennen-Befehl über das Datei-Kontextmenü ohne Key-Parameter aufgerufen wird. Die Funktion holt sich das aktive Steuerelement über Screen.ActiveControl:

Public Function DateiUmbenennen()
    Dim ctlTreeView As Object
    Set ctlTreeView = Screen.ActiveControl
    ctlTreeView.Object.StartLabelEdit
End Function

Das bereits aus Teil 1 bekannte Ereignis ctlTreeView_AfterLabelEdit im Formularmodul übernimmt dann den neuen Namen sowohl in die Tabelle tblFiles als auch im Dateisystem über den Aufruf von FS_DateiUmbenennen.

Eine Datei löschen

Die Funktion DateiLoeschen löscht eine Datei aus dem Dateisystem, aus der Tabelle tblFiles und aus dem TreeView-Steuerelement (Listing 2).

Sie übernimmt den Key des zu löschenden Elements, ermittelt daraus die ID und holt sich das aktive Steuerelement über Screen.ActiveControl.

Public Function DateiLoeschen(strKey As String)
    Dim db As DAO.Database
    Dim lngID As Long
    Dim ctlTreeView As Object
    Set ctlTreeView = Screen.ActiveControl
    lngID = Mid(strKey, 2)
    Set db = CurrentDb
    If FS_DateiLoeschen(lngID) = True Then
        db.Execute "DELETE FROM tblFiles WHERE FileID = " _
            & lngID, dbFailOnError
        ctlTreeView.Object.Nodes.Remove "i" & lngID
    End If
End Function

Nur wenn FS_DateiLoeschen den Wert True zurückgibt – also die Datei tatsächlich im Dateisystem gelöscht werden konnte – werden auch der Datenbank-Datensatz und der TreeView-Eintrag entfernt. Das vermeidet inkonsistente Zustände.

Eine Datei öffnen

Um eine Datei in der zugehörigen Standardanwendung zu öffnen, nutzen wir die Funktion DateiOeffnen. Sie ermittelt aus dem Key die ID, ruft GetFilePath auf und übergibt den vollständigen Pfad an FollowHyperlink:

Public Function DateiOeffnen(strKey As String) As Long
    Dim strPath As String
    Dim lngID As Long
    lngID = Mid(strKey, 2)
    strPath = GetFilePath(lngID)
    FollowHyperlink strPath
End Function

Das Ergebnis ist identisch mit dem Doppelklick-Verhalten, das wir bereits in Teil 1 beschrieben haben. Zusätzlich ist der Befehl nun auch über das Kontextmenü erreichbar.

Eine Datei einfügen

Die Funktion DateiEinfuegen ist die komplexeste der Datei-Kontextmenü-Funktionen, weil sie sowohl den Fall des Einfügens einer kopierten als auch einer ausgeschnittenen Datei behandelt (siehe Listing 2).

Public Function DateiEinfuegen() As Long
    Dim db As DAO.Database, ctlTreeView As Object, objSourceItem As MSComctlLib.Node
    Dim lngSourceID As Long, lngTargetID As Long
    Dim strSourceKey As String, strSourceText As String, strTargetKey As String
    Dim bolKopiert As Boolean, bolAusgeschnitten As Boolean, lngNewID As Long
    Set db = CurrentDb
    If Not Len(strKeyDateiKopiert) = 0 Then
        lngSourceID = Mid(strKeyDateiKopiert, 2)
        bolKopiert = True
    Else
        If Not Len(strKeyDateiAusgeschnitten) = 0 Then
            lngSourceID = Mid(strKeyDateiAusgeschnitten, 2)
            bolAusgeschnitten = True
        End If
    End If
    If Not lngSourceID = 0 Then
        Set ctlTreeView = Screen.ActiveControl
        strTargetKey = ctlTreeView.Object.SelectedItem.Key
        lngTargetID = Mid(strTargetKey, 2)
        If bolKopiert = True Then
            If FS_DateiKopieren(lngSourceID, lngTargetID) = True Then
                strSourceKey = "i" & lngSourceID
                Set objSourceItem = ctlTreeView.Object.Nodes(strSourceKey)
                strSourceText = objSourceItem.Text
                db.Execute "INSERT INTO tblFiles SELECT Filename, " _
                    & lngTargetID & " AS ParentID, FileSize, Filedatetime" _
                    & " FROM tblFiles WHERE FileID = " & lngSourceID, dbFailOnError
                lngNewID = db.OpenRecordset("SELECT @@IDENTITY").Fields(0)
                ctlTreeView.Object.Nodes.Add "f" & lngTargetID, tvwChild, _
                    "i" & lngNewID, strSourceText, "book"
            End If
        Else
            If FS_DateiVerschieben(lngSourceID, lngTargetID) = True Then
                strSourceKey = "i" & lngSourceID
                Set objSourceItem = ctlTreeView.Object.Nodes(strSourceKey)
                strSourceText = objSourceItem.Text
                db.Execute "UPDATE tblFiles SET ParentID = " & lngTargetID _
                    & " WHERE FileID = " & lngSourceID, dbFailOnError
                If db.RecordsAffected = 1 Then
                    ctlTreeView.Object.Nodes.Remove strSourceKey
                    ctlTreeView.Object.Nodes.Add "f" & lngTargetID, tvwChild, _
                        "i" & lngSourceID, strSourceText, "book"
                End If
            End If
        End If
    End If
End Function

Listing 2: Funktion zum Einfügen einer kopierten oder ausgeschnittenen Datei

Die Funktion prüft zunächst, ob eine Datei zum Kopieren oder zum Ausschneiden markiert ist. Das Ziel-Element – also der Ordner, in den eingefügt werden soll – wird über das aktuell im TreeView-Steuerelement selektierte Element bestimmt.

Die Quelle wird über die ID aus strKeyDateiKopiert beziehungsweise strKeyDateiAusgeschnitten ermittelt.

Beim Kopieren rufen wir FS_DateiKopieren auf. Ist das erfolgreich, legen wir in der Tabelle tblFiles einen neuen Datensatz an – mit einem INSERT INTO … SELECT-Ausdruck, der Dateiname, Größe und Datum übernimmt, aber die neue ParentID verwendet.

Die neue FileID ermitteln wir danach mit SELECT @@IDENTITY und fügen das Element unter dem Zielordner in das TreeView-Steuerelement ein.

Beim Ausschneiden rufen wir FS_DateiVerschieben auf. Nach erfolgreichem Verschieben aktualisieren wir die ParentID des vorhandenen Datensatzes in tblFiles per UPDATE.

Wir prüfen über db.RecordsAffected, ob genau ein Datensatz geändert wurde.

Ist das der Fall, entfernen wir den Node an seiner alten Position und fügen ihn beim Zielordner neu ein.


Nur für Abonnenten

Ab hier wird’s wirklich spannend – der Rest ist exklusiv für Abonnenten.

Mit dem Abo von Access im Unternehmen bekommst du den kompletten Artikel – inklusive vollständigem Code, Beispieldatenbank und Schritt-für-Schritt-Erklärung.

So sparst du dir stundenlanges Herumprobieren, vermeidest teure Fehler in deiner Access-Anwendung und kannst Lösungen direkt in deinem Unternehmen einsetzen, statt nur darüber zu lesen.

Teste Access im Unternehmen jetzt 4 Wochen lang kostenlos: Voller Zugriff auf alle Artikel, Downloads und Beispieldatenbanken. Kein Risiko – wenn es für dich nicht passt, kündigst du einfach innerhalb der ersten vier Wochen.

Bereits Abonnent? Hier einloggen


Kostenlos & unverbindlich

Oder hast Du eine konkrete Frage zu Deiner eigenen Access-Anwendung?

Vielleicht stellt Deine Anwendung Dich vor eine Herausforderung, zu der Du bisher keine Lösung findest. Schlechte Performance, kein ausreichender Zugriffsschutz, Du bist unsicher über Dein Datenmodell oder Dein Code liefert unerklärliche Fehler?

In unserem kostenlosen Access-Audit schaut sich André Minhorst persönlich gemeinsam mit Dir Deine Lösung per Zoom an – und zeigt Dir, wo Datenmodell, VBA-Code, Ergonomie und Sicherheit Optimierungspotenzial bieten.

Jetzt kostenloses Access-Audit anfordern →

Schreibe einen Kommentar