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).
Bild 1: Aufruf per Menüeintrag
Die zweite und bevorzugte Möglichkeit ist der Aufruf über das Kontextmenü des Codefensters (siehe Bild 2).
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.
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):