UserForms: Unterschied zwischen den Versionen
Pwania (Diskussion | Beiträge) |
Pwania (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
Zeile 1: | Zeile 1: | ||
[[Category:vba-wiki]] | |||
Zusätzlich zu den eingebauten Dialogen bietet VBA die Möglichkeit, eigene Dialoge zu erstellen. Diese werden 'UserForms' genannt und stellen eine Sonderform von Klassen dar. | Zusätzlich zu den eingebauten Dialogen bietet VBA die Möglichkeit, eigene Dialoge zu erstellen. Diese werden 'UserForms' genannt und stellen eine Sonderform von Klassen dar. | ||
Aktuelle Version vom 28. Januar 2023, 00:32 Uhr
Zusätzlich zu den eingebauten Dialogen bietet VBA die Möglichkeit, eigene Dialoge zu erstellen. Diese werden 'UserForms' genannt und stellen eine Sonderform von Klassen dar.
Einsatz von Dialogen
Prinzipiell können wir zwischen drei Dialogtypen unterscheiden, die in unserer VBA-Programmierung relevant sind:
- Mitteilung / einfache Entscheidung: Dem Anwender wird eine Nachricht angezeigt (MessageBox) oder eine Frage gestellt, die eindeutig beantwortet werden kann (Messagebox mit mehrern Knöpfen).
Beispiele:
MsgBox "Aufgabe erfolgreich erfüllt" blnContinue = MsgBox("Soll dieser Schritt ausgeführt werden?", vbYesNo)
- Eingabe: Der Anwender kann mehrere Daten ändern und den Dialog bestätigen oder den Vorgang abbrechen.
Beispiele: Eingabe / Bearbeitung von Daten, Einstellungen etc. - Komplexe Arbeitsschritte: Ein Dialog (oder mehrere Dialoge) führen durch einen komplexen / über mehrere Schritte verteilten Vorgang.
Beispiele: Seriendruck in Word
In diesem Kapitel werden wir uns ausschließlich mit Eingabedialogen befassen.
Trennung von Darstellung und Logik
Um den Überblick zu behalten und den Programmfluss optimal zu gestalten, sollte zwischen Darstellung (dem im Dialog Gezeigten) und der Logik (der Programmierung) eine Trennung erfolgen.
Diese Trennung kann anhand des eingebauten Datei-Dialogs veranschaulicht werden:
Public Function BrowseToPath() As String Dim dlg As FileDialog Set dlg = Application.FileDialog(msoFileDialogFolderPicker) dlg.Title = "Zielverzeichnis wählen" dlg.InitialFileName = "C:\temp\" dlg.Show If dlg.SelectedItems.Count > 0 Then BrowseToPath = dlg.SelectedItems.Item(1) End If End Function
- Eine Steuerprozedur (die Funktion 'BrowseToPath') fasst alle zur Anzeige und Auswertung des Dialogs benötigten Schritte zusammen.
- Die Anzeige des Dialogs erfolgt in vier Schritten:
1. Eine Variable wird deklariert und mit dem gewünschten Dialog instanziiert:
Dim dlg As FileDialog Set dlg = Application.FileDialog(msoFileDialogFolderPicker)
2. Der Dialog wird für die Anzeige vorbereitet:
dlg.Title = "Zielverzeichnis wählen" dlg.InitialFileName = "C:\temp\"
3. Der Dialog wird angezeigt:
dlg.Show
4. Die Benutzeringabe wird ausgewertet:
If dlg.SelectedItems.Count > 0 Then BrowseToPath = dlg.SelectedItems.Item(1) End If
Die Steuerprozedur ist demnach größtenteils für die Vorbereitung des Dialogs und die Reaktion auf / die Auswertung der Benutzereingaben beschäftigt. Die Erstellung des Dialogs und dessen Anzeige ergeben sich aus den Hauptaufgaben.
Wenn wir dieses System auf selbst erstellte Eingabedialoge anwenden, ergeben sich folgende Schritte:
Dialog (UserForm)
Arbeitsschritt | Beschreibung / Screenshot / Code | |
---|---|---|
1 | Dialog aufbauen | |
1a | Gestaltung des Dialogs (Anlegen und Platzierung der Elemente wie Beschriftungsfelder, Eingabefelder und Knöpfe). | |
1b | Sinnvolle Benennung der Elemente ('frmUserData', 'lblFirstName', 'txtFirstName', 'cmbOK', ...) vornehmen. | |
2 | Ereignisse einsetzen | |
2a | Das Klicken auf den OK-Knopf soll lediglich den Dialog verbergen, damit die Benutzereingaben ausgewertet werden können. Der Befehl 'Me.Hide' gibt die Befehlsgewalt direkt an die Prozedur zurück, welche 'myDialog.Show' aufgerufen hat. |
Private Sub cmbOK_Click() Me.Hide End Sub |
2b | Das Klicken auf den Abbrechen-Knopf soll ebenfalls den Dialog verbergen, aber zusätzlich signalisieren, dass der Vorgang abgebrochen wurde. Zusätzlich benötigen wir hierfür eine öffentliche Variable, damit die Steuerprozedur später diesen Wert abrufen kann. |
Public CancelWasPressed As Boolean Private Sub cmbCancel_Click() CancelWasPressed = True Me.Hide End Sub Achtung: Globale Variablen (hier 'Public CancelWasPressed As Boolean') müssen immer vor allen Prozeduren, am besten unter 'Option Explicit', deklariert werden! |
2c | Um das Beenden mit dem roten 'x' später als Abbruch des Dialogs auswerten zu können, benötigen Sie zusätzlich das UserForm-Ereignis 'Terminate', um auch hier die öffentliche Variable 'CancelWasPressed' auf 'True' zu setzen. |
Private Sub UserForm_Terminate() CancelWasPressed = True End Sub |
3 | Dialog füllen und auslesen | |
3a | Eine Prozedur ermöglicht es, dem Dialog die gewünschten Daten und Einstellungen für die Anzeige zu übergeben. | Einfache Übernahme von einem Wert:
Public Sub LoadName(ByVal strName As String) Me.txtName.Text = strName End Sub Übernahme von mehreren Werten aus einem benutzerdefinierten Datentyp 'Type': Public Sub LoadData(ByVal thisData As tpeDialogData) Me.txtFirstName.Text = thisData.FirstName Me.txtLastName.Text = thisData.LastName ' ... End Sub |
3b | Eine Funktion ermöglicht das Auslesen der vom Benutzer eingegebenen Werte. | Einfaches Auslesen eines Wertes:
Public Function ReturnName() As String ReturnName = Me.txtName.Text End Function Auslesen mehrerer Werte mithilfe eines benutzerdefinierten Datentyps 'Type': Public Function ReturnData() As tpeDialogData) ReturnDataMe.FirstName = txtFirstName.Text ReturnDataMe.LastName = txtLastName.Text ' ... End Function |
Steuerprozedur
Die Steuerprozedur übernimmt den Aufruf und die Kontrolle über den Dialog. Sie sorgt dafür, dass der Dialog die benötigten Daten erhält, damit er diese anzeigen kann, sie ruft den Dialog auf und sie liest anschließend die eingegebenen Daten aus und verarbeitet diese weiter.
Die Steuerprozedur kann auch eine Funktion sein, welche die Vorgabewerte als Parameter übernimmt und anschließend die Benutzereingabe als Rückgabewert zurück gibt.
Arbeitsschritt | Beschreibung / Screenshot / Code | |
---|---|---|
1 | Vorbereitung | |
1 a | Eine Variable zur Erstellung des Dialogs wird deklariert. |
Dim myDialog As frmDialog |
1 b | Die Variable wird mit 'New' instanziiert.
Das Schlüsselwort 'New' sorgt dafür, dass keine vorherige Instanz des Dialoges verwendet wird. Siehe New. |
Set myDialog = New frmDialog |
2 | Optional: Die Ausgangsdaten werden an den Dialog übergeben. Hierzu wird die entsprechende Prozedur im Dialog aufgerufen. Dieser Schritt wird nur benötigt, wenn Sie für die Anzeige des Dialogs zusätzliche Informationen benötigen oder Voreinträge machen möchten. |
myDialog.LoadName strName |
3 | Der Dialog wird angezeigt. |
myDialog.Show |
4 a | Wenn der Anwender nach der Anzeige mit 'Show' den Dialog schließt, kehrt die Ausführung in die Steuerprozedur zurück. Nun müssen wir klären, ob der Anwender 'Abbrechen' bzw. das rote 'X' oder 'OK' gewählt hat. Wurde der Dialog abgebrochen, soll damit in der Regel der weitere Verlauf der Prozedur ebenfalls abgebrochen werden. |
If myDialog.CancelWasPressed = True Then Exit Sub |
4 b | Der Dialog wird anschließend ausgewertet, also die Eingabe(n) des Anwenders abgefragt. |
strName = myDialog.ReturnName
|
- Beispiele einer Steuerprozedur
- Als Sub-Prozedur:
Public Sub ShowDialog() Dim strName As String Dim myDialog As frmDialog Set myDialog = New frmDialog myDialog.LoadData myDialog.Show If myDialog.CancelWasPressed = False Then strName = myDialog.ReturnName Debug.Print strName End If End Sub
- Als Funktion:
Public Function EnterName() As String Dim myDialog As frmDialog Set myDialog = New frmDialog myDialog.LoadData myDialog.Show If myDialog.CancelWasPressed = False Then EnterName = myDialog.ReturnName End If End Function
Intuitive Bedienung
Ein sehr wichtiger Aspekt bei der Gestaltung und Programmierung von Dialogen ist selbstverständlich die Bedienbarkeit. Hierzu dienen folgende Vorbereitungen:
OK-Knopf
Folgende Eigenschaft des OK-Knopfes sorgt dafür, dass bei der Betätigung der Eingabetaste (Enter, Return) der Dialog bestätigt wird, indem diese direkt an den OK-Knopf übergeben wird:
Default = True
Abbrechen-Knopf
Folgende Eigenschaft des Abbrechen-Knopfes sorgt dafür, dass bei der Betätigung der Escabe-Taste der Dialog abgebrochen wird, indem diese direkt an den Abbrechen-Knopf übergeben wird:
Cancel = True
Markierung des ersten Eingabefeldes
Um beim Starten des Dialogs das erste Eingabefeld zu markieren (damit der Anwender lediglich durch Eingabe eines Textes den ursprünglichen Wert überschreiben kann, ohne diesen erst löschen zu müssen), können Sie das 'Activate'-Ereignis der UserForm wie folgt erweitern:
Private Sub UserForm_Activate() txtFirst.SetFocus txtFirst.SelStart = 0 txtFirst.SelLength = Len(Me.txtFirst.Text) End Sub
Reihenfolge (TabIndex)
Die Reihenfolge beim Ausfüllen von Dialogen (durch Betätigung der Tabulator-Taste) wird mit der 'TabIndex'-Eigenschaft der Elemente bestimmt. Wenn Sie die Eingabefelder in der gewünschten Reihenfolge angelegt haben, müssen Sie diese in der Regel nicht nachbearbeiten. Wenn Sie jedoch nachträglich weitere Felder hinzufügen oder die Reihenfolge ändern, sollten Sie den Dialog daraufhin prüfen und die 'TabIndex'-Eigenschaft entsprechend anpassen.
Bitte beachten: Der erste TabIndex beträgt 0, alle weiteren werden um einen Index erhöht. Wenn Sie einen schon vorhandenen Index neu vergeben, erhält das Element mit dem doppelten Index einen neuen Index.
Bitte beachten: Der 'TabIndex' wird nicht beachtet, wenn die Eigenschaft 'TabStop' nicht 'True' ist!
Tipp: Die 'SetDefaultTabOrder'-Methode des UserForms weist allen Elementen des Dialogs eine neue Reihenfolge zu, die von links nach rechts und oben nach unten erfolgt.
Private Sub UserForm_Activate() Me.SetDefaultTabOrder End Sub
Eingabe zulassen / unterdrücken
Wenn Sie ausschließlich die Eingabe von vorgegebenen Zeichen in ein Eingabefeld (TextBox) zulassen möchten (zum Beispiel zur Eingabe einer Telefonnummer), können Sie das 'KeyPress'-Ereignis wie folgt einsetzen:
Private Sub txtTextbox_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger) ' Eingabe von nur diesen Zeichen zulassen If Not Chr(KeyAscii) Like "[0-9./+ -]" Then Beep ' Gibt einen Warnton aus KeyAscii = 0 End If End Sub
Wenn Sie die Eingabe von bestimmten Zeichen unterdrücken möchten, können Sie wie folgt vorgehen:
Private Sub txtValue_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger) ' Eingabe von diesen Zeichen unterdrücken If Chr(KeyAscii) Like "[.:-]" Then Beep ' Gibt einen Warnton aus KeyAscii = 0 End If End Sub
Elemente einer UserForm (Controls)
Listenfeld und Kombinationsfeld (ListBox und Combobox)
Das Listenfeld und das Kombinationsfeld sind sehr stark miteinander verwandt, denn beide stellen Datenreihen als Liste bzw. Tabelle dar. Der größte Unterschied ist die Darstellung: Das Listenfeld ist ein immer aufgeklapptes Kombinationsfeld, das Kombinationsfeld ein Listenfeld, bei dem nur der erste (bzw. gewählte) Eintrag sichtbar ist -- die weiteren Einträge können bei Bedarf sichtbar gemacht werden.
Listenfeld und Kombinationsfeld mit Daten füllen
Hierfür stehen insgesamt zwei (bei Excel drei) Wege zur Verfügung:
Aus einem Array
Wenn die Daten in Form eines Arrays vorliegen, können sie direkt in die 'List'-Eigenschaft übernommen werden:
Public Sub LoadData(ByRef strItems() As String) lbxListBox.List = strItems cboComboBox.List = strItems End Sub
Bitte beachten: Wenn ein mehrspaltiges Array übergeben wird, muss die 'ColumnCount'-Eigenschaft verwendet werden, um die zusätzlichen Spalten darzustellen.
Einzelne Elemente hinzufügen
Wenn die Daten als Collection vorliegen, können sie einzeln per 'AddItem' hinzugefügt werden:
Public Sub LoadData(ByVal colItems As Collection) Dim lngIndex As Long For lngIndex = 1 To colItems.Count lbxListBox.AddItem colItems.Item(lngIndex) cboComboBox.AddItem colItems.Item(lngIndex) Next lngIndex End Sub
Ebenso können einzelne Elemente, die nicht aus einer Collection stammen, zur Laufzeit immer wieder hinzugefügt werden.
Public Sub AddItem(ByVal strItem As String) lbxListBox.AddItem strItem cboComboBox.AddItem strItem End Sub
Aus einem Bereich einer Excel Tabelle
In die 'RowSource'-Eigenschaft kann ein beliebiger Bereich einer Excel-Tabelle als Datenquelle für ein Listen- bzw. Kombinationsfeld eingetragen werden. Hierbei ist zu beachten, dass die Daten für eine Liste aus einer Spalte stammen müssen. Wenn der Bereich eine Zeile darstellt, werden die Daten in eine Zeile übernommen und nicht untereinander dargestellt.
Public Sub LoadData(ByVal strRange As String) ' strRange könnte zum Beispiel "A1:A5" oder "A1:E5" enthalten lbxListBox.RowSource = strRange cboComboBox.RowSource = strRange End Sub
Bitte beachten: Wenn ein mehrspaltiger Bereich verwendet wird, muss die 'ColumnCount'-Eigenschaft angepasst werden, um die zusätzlichen Spalten darzustellen.
Beispiel: Nachbau der Inputbox
1. Die UserForm 'frmInputBox' wird dem Layout der Standard-Inputbox nachempfunden.
2. Die Knöpfe, das Bezeichnungsfeld und das Eingabefeld werden klar benannt und mit den gewünschten Eigenschaften versehen:
- Bezeichnungsfeld: 'lblPrompt'
- Eingabefeld (TextBox): 'txtInput'
- OK-Knopf: 'cmbOK', Eigenschaft 'Default' True
- Abbrechen-Knopf: 'cmbCancel', Eigenschaft 'Cancel' True
3. Die Ereignisse der UserForm werden ausformuliert. Sie rufen den Code-Editor der UserForm auf, indem Sie sie doppelt anklicken oder 'Ansicht', 'Code anzeigen' wählen.
Option Explicit Public CancelWasPressed As Boolean Private Sub cmbOK_Click() Me.Hide End Sub Private Sub cmbCancel_Click() CancelWasPressed = True Me.Hide End Sub Private Sub UserForm_Activate() txtInput.SelStart = 0 txtInput.SelLength = Len(Me.txtName) End Sub Private Sub UserForm_Terminate() CancelWasPressed = True End Sub
4. Eine Prozedur übernimmt Titel, Beschreibung und Vorgabetext in den Dialog. Der Titel wird nur übernommen, wenn er nicht leer ist.
Public Sub LoadData(ByVal strPrompt As String, ByVal strTitle As String, ByVal strDefault As String) lblPrompt.Caption = strPrompt If strTitle <> "" Then Me.Caption = strTitle txtInput.Text = strDefault End Sub
5. Eine Funktion gibt anschließend den eingegebenen Text zurück.
Public Function ReturnInput() As String ReturnInput = txtInput.Text End Function
6. Die Steuerprozedur ist in diesem Fall eine Funktion, welche den eingegebenen Wert zurückgibt. Wurde die Inputbox abgebrochen, wird, wie im Original, ein Leerstring zurückgegeben.
Public Function MyInputBox(ByVal strPrompt As String, ByVal strTitle As String, ByVal strDefault As String) As String Dim myDialog As frmInputBox Set myDialog = New frmInputBox myDialog.LoadData strPrompt, strTitle, strDefault myDialog.Show If myDialog.CancelWasPressed = False Then MyInputBox = myDialog.ReturnInput End If End Function
7. Die fertige Kopie der InputBox:
Debug.Print MyInputBox("Bitte etwas eingeben:", "Meine InputBox", "") ' Ergebnis: Test