Anlagen verwalten ohne Standarddialog

Wer den Anlagen-Datentyp und das dazugehörige Steuerelement mag, möchte vielleicht die Funktionen des Dialogs zum Verwalten von Anlagen gleich in eigene Formulare integrieren. Damit würde man auch die etwas unübersichtliche Darstellung in den Griff bekommen. Wie Sie ein solches Formular erstellen und einsetzen, lesen Sie in diesem Beitrag.

Das Anlage-Feld ist in der Standardansicht nicht besonders übersichtlich, erst nach dem Öffnen des Anlagen-Dialogs erscheint eine Liste der im Anlagefeld enthaltenen Dateien (s. Bild 1).

pic001.png

Bild 1: Anlage-Feld mit Anlagen-Dialog

Ziel dieses Beitrags ist, alle im Anlage-Feld gespeicherten Dateien gleich in einer Liste anzuzeigen und die Schaltflächen bereitzustellen, um Dateien hinzuzufügen, zu entfernen, zu öffnen, unter einem anderen Dateinamen zu speichern oder alle enthaltenen Dateien zu speichern – also genau wie im Standarddialog zur Verwaltung von Anlagen.

Um dieses Element einfach wiederverwertbar zu machen, verwenden wir dazu ein Unterformular, dem wir beim Öffnen einen Parameter mit dem Verweis auf das entsprechende Anlage-Feld übergeben. Das Unterformular soll die Dateien dann im einfachsten Fall in einem Listenfeld anzeigen. Denkbar wäre natürlich auch der Einsatz eines ListView-Steuerelements, das zusätzlich noch dem Dateityp entsprechende Icons anzeigen könnte.

Das benötigte Unterformular sieht im Entwurf wie in Bild 1 – mit Ausnahme der Schaltfläche Abbrechen, die ohnehin keinen anderen Nutzen als die OK-Schaltfläche besitzt.

pic002.png

Bild 2: Entwurf des Anlagen-Unterformulars

Das Hauptformular muss dem Unterformular irgendwie mitteilen, aus welchem Feld die anzuzeigenden Dateien eingelesen werden sollen. Da das übergeordnete Formular vermutlich einen Datensatz der Tabelle anzeigt, in dem sich das Anlagenfeld befindet, greifen wir entweder vom Unterformular auf das Hauptformular zu oder übergeben vom Hauptformular aus eine Referenz auf dieses Feld. Für den Moment verwenden wir im Unterformular zwei Objektvariablen namens m_AttachmentRecordset und m_Attachmentfield, die wie folgt im Klassenmodul des Formulars deklariert sind:

Dim m_Attachmentfield As DAO.Field2
Dim m_AttachmentRecordset As DAO.Recordset

Außerdem brauchen wir einen Verweis auf das Hauptformular, damit wir später auf das Wechseln des Datensatzes reagieren können:

Dim WithEvents m_Form As Form

Zusätzlich definieren wir eine öffentliche Prozedur zum Initialisieren des Formulars (s. Listing 1).

Listing 1: Initialisieren des Anlagen-Formulars

Public Sub Initialize(frm As Form, strAttachmentfield As String)
    Set m_Form = frm
    m_Form.OnCurrent = "[Event Procedure]"
    Set m_AttachmentRecordset = frm.RecordsetClone
    Set m_Attachmentfield = rstAttachment.Fields(strAttachmentfield)
    AnlagenAktualisieren
End Sub

Diese Prozedur speichert den Verweis auf das Formular, in dem sich das Unterformular befindet. Die Variable m_AttachmentRecordset wird mit einem Verweis auf den RecordsetClone des Hauptformulars gefüllt und m_AttachmentField mit einem Verweis auf das Anlage-Feld selbst.

Schließlich ruft die Prozedur noch eine weitere Routine namens AnlagenAktualisieren auf, die dafür verantwortlich ist, das Listenfeld mit den im Anlagefeld des aktuellen Datensatzes enthaltenen Anlagen zu füllen – dazu später mehr.

Listing 2: Anlagefeld aktualisieren

Private Sub AnlagenAktualisieren()
    m_AttachmentRecordset.Bookmark = m_Form.Recordset.Bookmark
    If Not m_Form.NewRecord Then
        AnlagenAuflisten
        Me!lstAnlagen = Me!lstAnlagen.ItemData(0)
    Else
        Me!lstAnlagen.RowSource = ""
    End If
    SchaltflaechenAktivieren
End Sub

Da das Formular frmAnlagen später als Unterformular dienen soll, fügen Sie es auch während der Entwicklung in ein weiteres Formular ein. In unserem Fall heißt dieses Formular frmKunden und zeigt die Daten der Tabelle tblKunden der Beispieldatenbank an (s. Bild 3).

pic004.png

Bild 3: Das Anlageformular als Unterformular

Fügen Sie das Unterformular in das Hauptformular ein und legen Sie für das Hauptformular eine Ereignisprozedur für die Eigenschaft Beim Laden an, die wie folgt aussieht:

Private Sub Form_Load()
    Me!frmAnlagen.Form.Initialize Me, "Dateianlagen"
End Sub

Diese Prozedur übergibt nur einen Verweis auf das Hauptformular sowie den Namen des Anlage-Feldes an die Initialize-Prozedur des Unterformulars.

Anlagen aktualisieren

Die oben referenzierte Prozedur AnlagenAktualisieren fasst einige Anweisungen zusammen, die sowohl beim erstmaligen Anzeigen des Anlagen-Listenfeldes, als auch beim späteren Datensatzwechsel im Hauptformular aufgerufen werden sollen (s. Listing 2). Die Prozedur prüft zunächst, ob das Hauptformular überhaupt einen Datensatz anzeigt. Falls ja, verschiebt es den Datensatzzeiger des unter m_AttachmentRecordset referenzierten Recordsets auf den aktuell im Formular angezeigten Datensatz und ruft die Routine AnlagenAuflisten auf, welche die Anlagen ausliest und das Listenfeld mit deren Dateinamen füllt. Zeigt das Formular einen neuen Datensatz an, wird lediglich das Listenfeld geleert. In beiden Fällen ruft die Prozedur die gleich vorgestellte Routine SchaltflaechenAktualisieren auf.

Listing 3: Füllen des Listenfeldes mit den Anlagen

Public Sub AnlagenAuflisten()
    Dim db As DAO.Database
    Dim rstAttachments As Recordset2
    Dim rstAttachmentsSortiert As Recordset2
    Set rstAttachments = m_Attachmentfield.Value
    rstAttachments.Sort = "Filename ASC"
    Set rstAttachmentsSortiert = rstAttachments.OpenRecordset
    Me!lstAnlagen.RowSource = ""
    Do While Not rstAttachments.EOF
         Me!lstAnlagen.AddItem rstAttachments!FileName
        rstAttachments.MoveNext
    Loop
End Sub

Liste mit Anlagen füllen

Die Prozedur AnlagenAuflisten füllt das Listenfeld des Formulars, das wir lstAnlagen nennen, mit den Dateinamen der im übergebenen Feld enthaltenen Dateien. Damit wir die AddItem und die RemoveItem-Methoden des Listenfeldes verwenden können, stellen wir die Eigenschaft Herkunftsart dieses Steuerelements auf Wertliste ein. Die Prozedur füllt dann das Listenfeld mit den Namen der im Anlagenfeld gespeicherten Dateien – und zwar in alphabetischer Reihenfolge (s. Listing 3). Das zukünftige Unterformular gestalten wir noch ein wenig übersichtlicher, indem wir die Eigenschaften Navigationsschaltfläche, Datensatzmarkierer und Bildlaufleisten jeweils auf den Wert Nein einstellen.

Listing 5: Auslösen des Hinzufügens einer Anlage aus einer Datei

Private Sub cmdHinzufuegen_Click()
    Dim objFiles As clsFiles
    Dim strDateien As String
    Dim strDateienArray() As String
    Dim i As Integer
    Set objFiles = New clsFiles
    With objFiles
        .Filter = "Alle Dateien (*.*)"
        .StartDir = CurrentProject.Path
        .Title = "Datei(en) auswählen"
        .MultipleFiles = True
        strDateien = .OpenFileName
    End With
    strDateienArray() = Split(strDateien, vbTab)
    For i = LBound(strDateienArray) To UBound(strDateienArray)
        AnlageHinzufuegen strDateienArray(i)
    Next i
    AnlagenAuflisten
End Sub

Steuerelemente aktivieren und deaktivieren

Bevor wir die Steuerelemente selbst mit Leben füllen, sollen diese zunächst, je nach den angezeigten Daten, aktiviert oder deaktiviert werden. Die Hinzufügen-Schaltfläche etwa ist immer aktiviert. Die Schaltfläche Entfernen hingegen soll nur benutzbar sein, wenn die Liste Dateien anzeigt und mindestens eine davon markiert ist. Das gleiche gilt für die Schaltflächen Öffnen und Speichern unter…. Die Schaltfläche Alle speichern wiederum darf ebenfalls immer aktiviert sein – außer wenn die Liste keinerlei Anlagen anzeigt.

Die Prüfung des Zustands des Listenfeldes und die davon abhängige Aktivierung/Deaktivierung der Schaltflächen nimmt die folgende Prozedur vor, die Sie ebenfalls im Klassenmodul des Formulars anlegen (s. Listing 4).

Listing 4: Aktiviere und Deaktivieren der Schaltflächen

Private Sub SchaltflaechenAktivieren()
    Dim bolDateiAusgewaehlt As Boolean
    bolDateiAusgewaehlt = Not Me!lstAnlagen.ItemsSelected.Count = 0
    Me!cmdEntfernen.Enabled = bolDateiAusgewaehlt
    Me!cmdOeffnen.Enabled = bolDateiAusgewaehlt
    Me!cmdSpeichernUnter.Enabled = bolDateiAusgewaehlt
    Me!cmdAllesSpeichern.Enabled = Not (Me!lstAnlagen.ListCount = 0)
End Sub

Diese Routine rufen wir zu verschiedenen Gelegenheiten auf, zum Beispiel in der Prozedur AnlagenAktualisieren. Auch nach dem ändern der Auswahl im Listenfeld sollen die Schaltflächen aktiviert beziehungsweise deaktiviert werden. Dies erledigt die Prozedur, die durch das Ereignis Nach Aktualisierung des Listenfeldes ausgelöst wird:

Private Sub lstAnlagen_AfterUpdate()
    SchaltflaechenAktivieren
End Sub

Hinzufügen von Anlagen

Ein Klick auf die Schaltfläche Hinzufügen… soll einen Datei auswählen-Dialog anzeigen, mit dem der Benutzer einen oder mehrere Dateien auswählt. Die Logik zum Anzeigen der Dateidialoge (wir werden später auch noch einen Datei speichern– und Verzeichnis auswählen-Dialog benötigen) liefert die Klasse clsFiles, die wir im Beitrag Dateidialog-Klasse vorstellen (www.access-im-unternehmen.de/)756.

Die Prozedur aus Listing 5 instanziert die Klasse, stellt einige Werte wie den Filter, den Titel, das Startverzeichnis und die Mehrfachauswahl ein und ruft den Dateidialog auf.

Listing 9: Öffnen der in einer Anlage enthaltenen Datei

Private Sub AnlageOeffnen()
    Dim objFiles As clsFiles
    Dim strAnlage As String
    If Me!lstAnlagen.ItemsSelected.Count > 1 Then
        MsgBox "Es wird nur die erste ausgewählte Anlage geöffnet."
    End If
    strAnlage = Me!lstAnlagen.ItemData(Me!lstAnlagen.ItemsSelected(0))
    AnlageSpeichern strAnlage, CurrentProject.Path & "\" & strAnlage
    Set objFiles = New clsFiles
    With objFiles
        .OpenDocument CurrentProject.Path & "\" & strAnlage
    End With
End Sub

Das Ergebnis, das in einer durch Tabulator-Zeichen getrennten Zeichenkette erscheint, wird dann mit der Split-Funktion in die einzelnen Dateinamen zerlegt. Das Speichern der angegebenen Dateien im Anlagefeld übernimmt dann die Prozedur AnlageHinzufuegen (mehr dazu gleich).

Die Prozedur AnlageHinzufuegen (s. Listing 6) fügt jeweils eine Anlage zum Anlagefeld hinzu. Dabei erwartet sie Pfad und Dateiname der hinzuzufügenden Anlage. Anlagefelder speichern die Anlagen in einer eigenen Tabelle, über die man genau wie auf eine normale Tabelle per Recordset2-Objekt zugreifen kann (die neue Variante des Recordset-Objekts). Dieses Recordset2-Objekte füllen Sie schlicht durch Zuweisen der Value-Eigenschaft des Feldes mit dem Datentyp Anlage. Dieses Recordset2-Objekt enthält dann bestimmte Felder wie FileData, FileName et cetera, auf die Sie ganz normal wie auf handelsübliche Recordset-Felder zugreifen können.

Listing 6: Hinzufügen einer einzelnen Datei als Anlage

Private Sub AnlageHinzufuegen(strPfadUndDatei As String)
    Dim rstAttachments As DAO.Recordset2
    Dim strDatei As String
    strDatei = Mid(strPfadUndDatei, InStrRev(strPfadUndDatei, "\") + 1)
    Set rstAttachments = m_Attachmentfield.Value
    m_AttachmentRecordset.Edit
    rstAttachments.FindFirst "Filename = ''" & strDatei & "''"
    If Not rstAttachments.NoMatch Then
        If MsgBox("Die Anlage ''" & strDatei & "'' ist bereits vorhanden. Möchten Sie diese Anlage " _
                & "überschreiben", vbYesNo) = vbYes Then
            rstAttachments.Delete
        Else
            Exit Sub
        End If
    End If
    rstAttachments.AddNew
    On Error Resume Next
    rstAttachments!FileData.LoadFromFile strDatei
    If Err.Number = -2146697202 Then
         Name strDatei As strDatei & ".dat"
        rstAttachments!FileData.LoadFromFile strDatei & ".dat"
        rstAttachments!FileName = Mid(strDatei, InStrRev(strDatei, "\") + 1)
        Name strDatei & ".dat" As strDatei
    End If
    rstAttachments.Update
    m_AttachmentRecordset.Update
End Sub

Um aber die Datensätze des Recordsets eines Attachment-Feldes mit DAO-Befehlen wie AddNew, Edit oder Update zu bearbeiten, müssen Sie zunächst das Recordset mit Edit in den Bearbeitungsmodus versetzen, welches das Attachment-Feld enthält, und Sie müssen anschließend die änderungen mit Update in die Tabelle schreiben. Dies geschieht auch in der Prozedur AnlageHinzufuegen. Das Recordset m_AttachmentRecordset enthält Datensätze der eigentlichen Tabelle, rstAttachments die Datensätze des Attachment-Feldes.

Die Prozedur durchsucht das Feld FileName der Attachment-Datensätze nach dem Namen der hinzuzufügenden Datei. Ist bereits eine Anlage mit diesem Namen vorhanden, soll der Benutzer entscheiden, ob diese überschrieben werden soll. Falls ja, löschen wir diesen Datensatz kurzerhand mit der Delete-Methode, anderenfalls ist die Prozedur hier zuende.

War noch kein gleichnamiges Attachment vorhanden oder soll das vorhandene überschrieben werden, wird im Attachment-Recordset ein neuer Datensatz angelegt. Das Hinzufügen der Datei erledigt dann die Methode LoadFromFile des Feldes FileData.

Hier kann lediglich noch ein kleines Problem auftauchen: Access erlaubt nicht alle Dateiendungen für Anlagefelder. Da es aber nicht untersucht, was sich tatsächlich in einer Datei befindet, ändert die Prozedur die Dateiendung einfach, indem sie .dat an den Dateinamen anhängt. Danach erfolgt das Hinzufügen der Datei ohne Probleme, sodass die Prozedur nur noch den Namen der Ursprungsdatei zurückändern und die im Feld FileName gespeicherte Information entsprechend anpassen muss.

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