Dateiauswahl-Dialog-Assistent programmieren

Lies in den Artikel rein und unten bekommst Du ein unschlagbares Angebot!

Dateidialoge benötigt man immer wieder. Ob man nun Dateien zum Öffnen oder Bearbeiten auswählen möchte, ob man einen Pfad zum Speichern einer Datei braucht oder ob man ein Verzeichnis selektieren will – am einfachsten geht das mit den praktischen Dateidialogen. Die Office-Bibliothek bietet sogar alle benötigten Varianten über die FileDialog-Klasse an. Dumm ist nur, dass man nicht ständig Filedialoge programmiert, sondern nur alle paar Wochen, Monate oder sogar Jahre. Dann muss man sich immer wieder einarbeiten, um die verschiedenen Parameter – Titel, Schaltflächenbeschriftungen, Standardverzeichnis und -dateiname, Filter, Dateiendungen und so weiter zu definieren. Wie schön wäre es doch, wenn wir solche Dateidialoge mit einem kleinen Assistenten zusammenstellen könnten. Also machen wir uns ans Werk und schaffen einen solchen Wizard!

Wann, wie und wo nutzen wir den Dateidialog-Assistenten?

Die erste Frage, die sich stellt, ist: Wo und wie wollen wir diesen Assistenten aufrufen und was genau soll dieser eigentlich produzieren? Als Erstes fällt uns ein, dass er eine Funktion liefern soll, die den Dateidialog aufruft und uns die gewünschte Datei oder das Verzeichnis zurückliefert.

Wir könnten uns also darauf beschränken, diesen Assistenten für das Zusammenstellen des benötigten Codes zu nutzen und diesen dann beispielsweise an der aktuellen Stelle im Codefenster des VBA-Editors einzufügen oder diesen in die Zwischenablage zu schreiben, damit wir diesen selbst einfügen können.

In vielen Fällen mag das ausreichen: Dann ruft man den Dateidialog aus einer anderen Prozedur heraus auf und verwendet die gelieferten Informationen in den folgenden Codezeilen für den gewünschten Zweck.

In anderen Fällen spielt die Benutzeroberfläche eine Rolle: Dann möchte man vielleicht in einem Access-Formular ein Verzeichnis etwa zum Exportieren von Daten anzeigen und auswählen. Dazu fügt man diesem ein Textfeld oder ein Bezeichnungsfeld zur Anzeige des Pfades hinzu und legt daneben eine Schaltfläche an, mit der man den Dateiauswahl-Dialog aufruft.

Letztlich würde auch hier erst einmal der Assistent zum Zusammenstellen von Funktionen zum Anzeigen von Dateidialogen ausreichen.

Man könnte die benötigten Steuerelemente und die Programmlogik zum Aufruf der Funktionen dann selbst hinzufügen – immerhin ist das der wesentlich weniger aufwendige Teil dieser Aufgabe.

Wir werden uns also zunächst darauf beschränken, den Code für die Anzeige von Dateidialogen abhängig von den gewünschten Parametern zu produzieren.

Wo wollen wir diese Funktionalität bereitstellen? Sinnvoll ist ein COM-Add-In für den VBA-Editor, denn dort werden wir den zu erstellenden Code auch nutzen.

Wo dort wollen wir die Funktion aufrufen? Hier gibt es drei mögliche Stellen:

  • einen Menüeintrag, beispielsweise unterhalb des Eintrags Add-Ins oder in einem eigenen Hauptmenü
  • ein Eintrag in der Symbolleiste
  • ein Eintrag im Kontextmenü, das erscheint, wenn wir mit der rechten Maustaste in das Codefenster klicken

Werkzeug für das Erstellen des COM-Add-Ins

Die Entwicklungsumgebung und Programmiersprache twinBASIC hat sich als zuverlässiges Tool für die Programmierung von COM-Add-Ins und COM-DLLs für Office-Anwendungen und den VBA-Editor erwiesen, sodass wir diesen auch in diesem Artikel für die Erstellung des gewünschten Assistenten nutzen werden.

COM-Add-In bis zur eigentlichen Funktion

Im Artikel twinBASIC: COM-Add-In für den VBA-Editor (www.access-im-unternehmen.de/421) stellen wir eine Vorlage für die Programmierung von COM-Add-Ins für den VBA-Editor vor – und erläutern, welche Schritte nötig sind, um diese Vorlage in ein eigenes, neues COM-Add-In umzuwandeln.

Genau das haben wir gemacht und so ein neues, fast leeres COM-Add-In erzeugt. Die einzigen Elemente, die dieses COM-Add-In enthält, dienen zur Anzeige von Menüeinträgen in einem neuen Menüpunkt im Hauptmenü des VBA-Editors sowie eines Eintrags eines Kontextmenüs, das erscheint, wenn wir mit der rechten Maustaste in das Codefenster klicken.

Informationen zum Erstellen des Codes ermitteln

Bevor wir starten, stellen wir eine Liste der Informationen zusammen, die wir vor dem Erstellen des Codes für die Anzeige eines Dateiauswahl-Dialogs vom Entwickler ermitteln müssen.

Was benötigen wir also alles?

  • Um was für einen Dateidialog soll es sich handeln? Es gibt Dialoge für die Auswahl von bestehenden Dateien, für zu erstellende Dateien und für Verzeichnisse. Dies können wir mit einer Optionsgruppe oder einem Kombinationsfeld abfragen. Außerdem gibt es noch den Typ zum direkten Öffnen von Dateien, der in Word oder Excel zum Einsatz kommen kann.
  • Einfache oder Mehrfachauswahl. Soll der Benutzer keine, eine oder sogar mehrere Dateien auswählen können? Wenn er mehrere Dateien auswählen können soll, müssen wir uns überlegen, wie wir diese Dateien übergeben.
  • Beschriftung des Dialogtitels
  • Beschriftung der Schaltfläche zum Auswählen
  • Ordner, der beim Öffnen des Dialogs angezeigt werden soll, einstellen
  • Datei, die beim Öffnen ausgewählt oder angegeben werden soll
  • Leeren vorhandener Filter, Erstellen von Filtern und Auswahl eines der Filter als Standard

Auf all diese Elemente gehen wir in den folgenden Abschnitten ein.

Der FileDialog-Wizard in Aktion

Den Wizard wollen wir auf zwei Arten aufrufen können. Die erste ist der Menüeintrag AMV-Tools|amvFileDialogWizard (siehe Bild 1).

Aufruf per Menüeintrag

Bild 1: Aufruf per Menüeintrag

Die zweite und bevorzugte Möglichkeit ist der Aufruf über das Kontextmenü des Codefensters (siehe Bild 2).

Aufruf per Kontextmenü

Bild 2: Aufruf per Kontextmenü

In Bild 3 zeigen wir, wie der fertige Wizard aussehen soll. Hier sehen wir Eingabefelder für den Titel, den Funktionsnamen, den Buttontext und den beim Öffnen anzuzeigenden Pfad. Für Dialogtyp haben wir ein Kombinationsfeld hinterlegt.

Der FileDialog-Wizard in Aktion

Bild 3: Der FileDialog-Wizard in Aktion

Ob der Dialog die Mehrfachauswahl unterstützen soll, legen wir mit einer CheckBox fest. Schließlich kann der Benutzer die Beschreibung und die Dateiendung für Filter über zwei Textfelder eingeben, die mit der Plus-Schaltfläche zur Liste der anzuzeigenden Filter hinzugefügt werden. Mit der Minus-Schaltfläche kann der Benutzer den markierten Filtereintrag wieder entfernen.

Jede Änderung, die der Benutzer an einem der Steuerelemente vornimmt, wirkt sich direkt auf den angezeigten Code aus. Dadurch sieht der Benutzer jederzeit, welcher Code eingefügt wird, wenn er auf die OK-Schaltfläche klickt.

Programmierung des COM-Add-Ins zur Bereitstellung des FileDialog-Wizards

Diesen Wizard programmieren wir in den folgenden Abschnitten.

Neues Projekt erstellen

Als Erstes erstellen wir ein neues Projekt auf Basis der Vorlage, die wir im Artikel twinBASIC: COM-Add-In für den VBA-Editor (www.vbentwickler.de/421) beschrieben haben. In diesem Artikel haben wir auch gezeigt, an welchen Stellen Änderungen vorgenommen werden müssen, wenn wir ein neues COM-Add-In auf der Basis der Vorlage erstellen wollen. Der erste Schritt ist, dass wir die Hauptklasse in FileDialogWizard umbenennen und das auch für einige weitere Elemente im Projekt erledigen.

Viele der Elemente können wir zunächst beibehalten. Dazu gehört auch die Prozedur CreateToolbars. Hier haben wir lediglich die beiden Konstanten geändert, welche für die Anzeige des Menü- und des Kontextmenüeintrags verantwortlich sind. Diese lauten nun wie folgt:

Private Const cStrMenu As String = "AMV-Tools"
Private Const cStrCommandButton As String = "amvFileDialogWizard"

Für die beiden Ereignisprozeduren, die durch die beiden Schaltflächen im Menü und im Kontextmenü ausgelöst werden, haben wir jeweils den Aufruf der Prozedur LaunchFileDialogWizard eingetragen:

Private Sub cbbEvents_Click(...) Handles _
         cbbEvents.Click
     LaunchFileDialogWizard
     ...
End Sub
Private Sub cbbEventsContext_Click(...) _
         Handles cbbEventsContext.Click
     LaunchFileDialogWizard
     ...
End Sub

Damit sind die Arbeiten am Modul FileDialogWizard bereits erledigt.

Die Methode LaunchFileDialogWizard

Die durch die Menübefehle aufgerufene Methode LaunchFileDialogWizard finden wir in Listing 1. Sie ist dafür verantwortlich, das Fenster unseres Assistenten anzuzeigen und den Code anzuhalten, bis dieses Fenster entweder geschlossen oder ausgeblendet wird. Das Fenster initialisieren wir auf Basis des Form-Objekts frmFileDialogWizard, das wir weiter unten erläutern. An dieser Stelle ist erst einmal wichtig, wie wir es aufrufen, wie wir reagieren, wenn es den Fokus verliert und was dann geschieht. Bevor wir es anzeigen, speichern wir sein Handle in der Variablen lngHwnd. Dann öffnen wir es mit der Show-Methode und übergeben dieser den Wert vbModal als Parameter, damit der aufrufende Code angehalten wird, bis der Benutzer die Arbeit mit dem Fenster abschließt.

Public Sub LaunchFileDialogWizard()
     Dim frm As frmFileDialogWizard
     Dim strCode As String
     Dim lngStartLine As Long
     Dim lngStartColumn As Long
     Dim lngEndLine As Long
     Dim lngEndColumn As Long
     Dim lngCurrentLine As Long
     Dim objCodepane As CodePane
     Dim objCodemodule As CodeModule
     Dim lngHwnd As Long
     Set frm = New frmFileDialogWizard
     lngHwnd = frm.hWnd
     frm.Show vbModal
     If IsWindow(lngHwnd) = 0 Then
         Exit Sub
     Else
         strCode = frm.txtCode
         UnloadObjectByHandle(lngHwnd)
         Set objCodepane = objVBE.ActiveCodePane
         Set objCodemodule = objCodepane.CodeModule
         objCodepane.GetSelection(lngStartLine, lngStartColumn, _
             lngEndLine, lngEndColumn)
         objCodemodule.InsertLines(lngStartLine, strCode)
     End If
End Sub

Listing 1: Die Prozedur LaunchFileDialogWizard

Dann prüfen wir mit der Funktion IsWindow, ob das Fenster noch geöffnet ist oder nicht. Im ersten Fall hat der Benutzer das Fenster mit der Abbrechen-Schaltfläche oder der Schließen-Schaltfläche geschlossen. Dann gibt es nichts weiter zu tun, als die Prozedur zu beenden.

Anderenfalls lesen wir den im Textfeld txtCode enthaltenen Code in die Variable strCode ein und schließen das Formular mit der Funktion UnloadObjectByHandle, wobei wir das Handle des Fensters übergeben.

Danach referenzieren wir das aktuelle Codepane– und dann das CodeModule-Objekt. Über die Methode GetSelection des CodePane-Elements lesen wir die aktuelle Position der Markierung aus. Hier wollen wir anschließend den im Wizard zusammengestellten Code einfügen. Um die Markierung zu speichern, übergeben wir dieser Methode die Parameter lngStartLine, lngStartColumn, lngEndLine und lngEndColumn, die von dieser Methode gefüllt werden sollen.

Wir benötigen davon nur den Parameter lngStartLine, denn in dieser Zeile wollen wir den Code mit der InsertLines-Methode einfügen.

Das war einfach bisher – aber nun kommen wir zur eigentlichen Funktion.

Klasse zum Erstellen des FileDialog-Codes

Die Klasse clsFileDialogWizard ist das Arbeitstier des COM-Add-Ins. Die Klasse nimmt alle notwendigen Informationen entgegen und erstellt den Code der Funktion zum Anzeigen des Dateidialogs. Dazu deklarieren wir in der Klasse zunächst die folgenden lokalen Variablen:

Private m_Title As String
Private m_Type As MsoFileDialogType
Private m_FunctionName As String
Private m_AllowMultiSelect As Boolean
Private m_Separator As String
Private m_ButtonName As String
Private m_InitialFileName As String
Private m_Filter As String

Diese füllen wir über entsprechende Property Let-Prozeduren, die nach außen als Eigenschaften präsentiert werden. Für die meisten Eigenschaften ist das recht einfach – sie nehmen einfach den Wert entgegen und speichern diesen in der jeweiligen Variablen:

Public Property Let Title(str As String)
     m_Title = str
End Property
Public Property Let Type(lng As MsoFileDialogType)
     m_Type = lng
End Property
Public Property Let FunctionName(str As String)
     m_FunctionName = str
End Property
Public Property Let AllowMultiSelect(bol As Boolean)
     m_AllowMultiSelect = bol
End Property
Public Property Let Separator(str As String)
     m_Separator = str
End Property
Public Property Let ButtonName(str As String)
     m_ButtonName = str
End Property
Public Property Let InitialFilename(str As String)
     m_InitialFileName = str
End Property

Nur für den Filter ist es etwas komplizierter. Hierfür definieren wir die Property Let-Prozedur aus Listing 2. Sie nimmt eine Variable des Typs Variant entgegen. Der Inhalt ist ein Array mit zwei Dimensionen, wobei die Zeilen jeweils die Beschreibung und die Dateiendung für den Filter enthalten.

Public Property Let Filter(var As Variant)
     Dim strTemp As String
     Dim i As Integer
     If IsEmpty(var) Then
         m_Filter = ""
         Exit Property
     End If
     strTemp = "        .Filters.Clear" & vbCrLf
     For i = LBound(var) To UBound(var)
         strTemp += "        .Filters.Add """ & var(i, 1) _
             & """, """ & var(i, 2) & """" & vbCrLf
     Next
     m_Filter = strTemp
End Property

Listing 2: Die Prozedur Filter

Die Prozedur stellt die Anweisungen zum Einstellen des Filters zusammen und speichert diese in der Variablen m_Filter. Dabei schreibt sie in die erste Zeile die Anweisung zum Leeren des Filters (.Filters.Clear).

Danach durchläuft sie die Zeilen des Arrays in einer For…Next-Schleife und fügt für jede Zeile einen Eintrag wie den folgenden zum Filterausdruck hinzu – zum Beispiel:

.Filters.Clear
.Filters.Add "Alle Dateitypen", "*.*"

Zusammenstellen der FileDialog-Funktion

Die Hauptarbeit in der Klasse übernimmt die Methode CreateFileDialogCode, die wir in Listing 3 zeigen. Sie deklariert eine Variable namens strCode, in der sie den kompletten Code zusammenträgt. Den Start macht die Kopfzeile der Prozedur, die aus Public Function und dem Namen der Funktion aus m_FunktionName besteht. Jede Zeile wird mit einem Zeilenumbruch abgeschlossen (vbCrLf).

Public Function CreateFileDialogCode() As String
     Dim strCode As String
     strCode = "Public Function " & m_FunctionName & "()" & vbCrLf
     strCode += "    Dim objFileDialog As Office.FileDialog" & vbCrLf
     strCode += "    Dim strTemp As String" & vbCrLf
     strCode += "    Set objFileDialog = Application.FileDialog(" & GetTypeConstant(m_Type) & ")" & vbCrLf
     strCode += "    With objFileDialog" & vbCrLf
     If Not Len(m_Title) = 0 Then
         strCode += "        .Title = """ & m_Title & """" & vbCrLf
     End If
     If Not Len(m_ButtonName) = 0 Then
         strCode += "        .ButtonName = """ & m_ButtonName & """" & vbCrLf
     End If
     If m_AllowMultiSelect = True Then
         strCode += "        .AllowMultiSelect = True" & vbCrLf
     End If
     If Not Len(m_InitialFileName) = 0 Then
         strCode += "        .InitialFilename = """ & m_InitialFileName & """" & vbCrLf
     End If
     strCode += m_Filter
     strCode += "        If .Show = True Then" & vbCrLf
     If m_AllowMultiSelect Then
         strCode += "            For Each varFilename In .SelectedItems " & vbCrLf
         strCode += "                strTemp = strTemp & """ & m_Separator & """ & varFilename" & vbCrLf
         strCode += "            Next varFilename" & vbCrLf
         strCode += "            If Not Len(strTemp) = 0 Then " & vbCrLf
         strCode += "                strTemp = Mid(strTemp, 2)" & vbCrLf
         strCode += "            End If" & vbCrLf
     Else
         strCode += "            strTemp = .SelectedItems(1) " & vbCrLf
     End If
     strCode += "        End If" & vbCrLf
     strCode += "    End With" & vbCrLf
     strCode += "    " & m_FunctionName & " = strTemp" & vbCrLf
     strCode += "End Function"
     Return strCode
End Function

Listing 3: Die Prozedur CreateFileDialog

Die folgenden Zeilen fügen die Deklaration der Variablen für das FileDialog-Objekt und einer temporären String-Variablen hinzu, bevor die Zeile zum Initialisieren des FileDialog-Objekts eingefügt wird. In dieser haben wir einen weiteren variablen Anteil, nämlich den Typ des FileDialog-Elements. Dieser kann mit der Eigenschaft Type übergeben werden und landet in m_Type.

Um diese auszuwerten, verwenden wir eine Hilfsfunktion namens GetTypeConstant, der wir den MsoFileDialogType übergeben und die eine entsprechende Zeichenkette zurückliefert:

Public Function GetTypeConstant( _
         lngType As MsoFileDialogType) As String
     Select Case lngType
         Case MsoFileDialogType.msoFileDialogFilePicker
             Return "msoFileDialogFilePicker"
         Case MsoFileDialogType.msoFileDialogFolderPicker
             Return "msoFileDialogFolderPicker"
         Case MsoFileDialogType.msoFileDialogOpen
             Return "msoFileDialogOpen"
         Case MsoFileDialogType.msoFileDialogSaveAs
             Return "msoFileDialogSaveAs"
     End Select
End Function

Danach folgt die Zuweisung der eigentlichen Eigenschaften. Wenn m_Title einen Wert enthält, wird die Zeile .Title = [Titel] hinzufügt (der Teil [Titel] ist als Platzhalter zu verstehen). Ist ein Wert für ButtonName übergeben worden, resultiert dies in der Anweisung .ButtonName = [ButtonName]. Hat m_AllowMultiSelect den Wert True, wird dies mit .AllowMultiSelect = True vermerkt. Diese Einstellung wird gleich nochmals berücksichtigt. Wurde ein initial anzuzeigender Pfad angegeben, landet dieser in der Zeile .InitialFilename = [Pfad]. Anschließend wird der zuvor bereits zusammengestellte und in m_Filter gespeicherte Teil mit den Anweisungen für den Filter hinzugefügt.

Danach kommen wir zum zweiten Teil, der durch die Eigenschaft AllowMultiSelect beeinflusst wird. Diese Option legt fest, dass nicht nur eine, sondern gegebenenfalls auch mehrere Dateien ausgewählt werden können. Dem müssen wir bei der Auswertung des Dateiauswahl-Dialogs Rechnung tragen – und somit auch in der Prozedur, die den Code für diese Funktion zusammenstellt.

In diesem Fall fügen wir, falls m_AllowMultiSelect den Wert True hat, eine For Each-Schleife hinzu, die alle Einträge aus SelectedItems durchläuft und sie in einer durch ein Trennzeichen separierten Liste zusammenstellt. Dabei fügen wir das Trennzeichen für jeden Eintrag voran und entfernen diesen für das erste Element anschließend wieder.

Wenn m_AllowMultiSelect den Wert False hat, fügen wir einfach die Anweisung strTemp = SelectedItems(1) hinzu. Schließlich schließen wir die noch offenen If– und With-Konstrukte und fügen die letzte Anweisung hinzu, welche das Ergebnis aus strTemp dem Namen Funktion zuweist. Das Ergebnis wird durch die Return-Anweisung an die aufrufende Funktion zurückgegeben.

Erstellen des Form-Elements

Damit kommen wir zur grafischen Benutzeroberfläche des COM-Add-Ins. Diese besteht aus einem Form-Element, das wir über den Menüeintrag Project|Add|Add Form hinzufügen. Dem Form-Element fügen wir verschieden Steuerelemente hinzu (siehe Bild 4):

 

Schreibe einen Kommentar