UserForms

Aus VBA-wiki
Version vom 28. Januar 2023, 00:32 Uhr von Pwania (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

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.

InputBox 01.png

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

InputBox 02.png