Listenauswahl mit Tastencodes optimieren

15.12.2006 von Helma  Spona
Auswahllisten sind für Anwender optimal, weil sie Fehler vermeiden helfen, dennoch sind sie gerade dann, wenn massenhaft Daten erfasst werden, eher unbeliebt, weil zwischen den Tastatureingaben zur Listenauswahl immer wieder der Griff zur Maus notwendig wird. Beheben lässt sich das Problem mit ein paar Einstellungen und etwas VBA-Code.

Das erste Ärgernis bei Pulldown-Listen ist aus Anwendersicht, dass sich die Liste nicht öffnet, wenn das Steuerelement mit Hilfe der [Tab]-Taste angesprungen wird und den Fokus erhält.

Dieses Problem lässt sich ganz einfach mit Hilfe einer Ereignisprozedur für das Ereignis BeiFokuserhalt (GotFocus) aus der Welt schaffen (Listing 1).

Private Sub Typ_GotFocus()
Me.Typ.Dropdown
End Sub

  1. Markieren Sie das Kombinationssteuerelement in der Entwurfsansicht des Formulars.

  2. Wechseln Sie im Eigenschaftenfenster auf die Registerkarte Ereignis, und wählen Sie aus der Auswahlliste für das Ereignis BeiFokuserhalt den Eintrag [Ereignisprozedur] aus.

  3. Klicken Sie danach auf die Schaltfläche Durchsuchen, um die Ereignisprozedur zu erstellen.

  4. Ergänzen Sie diese wie in Listing 1 durch die Anweisung Me.XXX.DropDown, wobei XXX durch den Namen des Steuerelements zu ersetzen ist.

Enthält Ihr Formular viele verschiedene Auswahllisten, müssen Sie für diese eine Anweisung ganz viele Ereignisprozeduren erstellen. In diesem Fall kann es günstiger sein, den alternativen Weg zu wählen und alle Tastendrücke innerhalb des Formulars abzufangen und auszuwerten.

Dazu müssen Sie zunächst die Tastenvorschau für das Formular aktivieren. Sie bewirkt, dass die Tastendrücke zunächst von den EventHandlers des Formulars und erst dann von denen der Steuerelemente abgearbeitet werden.

  1. Markieren Sie in der Entwurfsansicht des Formulars das komplette Formular

  2. Aktivieren Sie im Eigenschaftenfenster die Registerkarte Ereignis und wählen Sie für die Eigenschaft Tastenvorschau den Wert Ja aus.

  3. Wählen Sie nun für die Eigenschaft BeiTaste den Eintrag [Ereignisprozedur] aus, und klicken Sie auf die Durchsuchen-Schaltfläche (Bild 1).

Bild 1: Tastenvorschau aktivieren und Ereignisprozedur erstellen.

Die Ereignisprozedur ergänzen Sie dann durch den Code in Listing 2. Er prüft beim Drücken einer Taste, ob es sich um die [Tab]-Taste handelt. Diese wird üblicherweise verwendet, um zwischen den Steuerelementen zu wechseln. Wurde die [Tab]-Taste gedrückt, wird die Dropdown- Methode des aktuellen Steuerelements aufgerufen. Damit es dabei nicht zu einem Laufzeitfehler kommt, wenn es sich bei dem Steuerelement nicht um ein Kombinationslistenfeld handelt, müssen Sie allerdings vorher die Anweisung On Error Resume Next angeben.

Nun werden automatisch alle Dropdown-Listenfelder geöffnet, wenn sie mit der [Tab]-Taste aktiviert werden, ohne dass Sie dazu für jedes Listenfeld eine Ereignisprozedur erstellen müssen.

Private Sub Form_KeyPress(KeyAscii As Integer)
If KeyAscii = vbKeyTab Then
On Error Resume Next
Me.ActiveControl.Dropdown
End If
End Sub

Zeilen auswählen

Ist die Liste einmal geöffnet, kann der Benutzer durch Eingabe des ersten Zeichens eines Wertes in der ersten Spalte den entsprechenden Eintrag auswählen und die Auswahl mit den Pfeiltasten nach oben oder unten bewegen und mit [Strg]+[Pos1] beziehungsweise [Strg]+[Ende] zum ersten oder letzten Listeneintrag springen.

Was noch verbesserungswürdig ist, ist die Auswahl anhand von Werten in der zweiten oder einer anderen Spalte. Bei längeren Listen, beispielsweise zur Auswahl eines Kunden oder Artikels, kann die Auswahl über die Nummer, die meist in der ersten Spalte steht, ungünstig sein. Hier können Sie mit etwas VBA-Programmierung nachhelfen.

Sie benötigen dazu eine Funktion (Listing 3), die den passenden Listeneintrag in einem Listenfeld sucht und den gefundenen Eintrag als aktiven Eintrag festlegt. Wenn Sie die Funktion in mehreren Steuerelementen einsetzen möchten, sollten Sie das Steuerelement, die zu durchsuchende Spaltennummer und den zu suchenden Wert an die Funktion übergeben (Listing 3). Innerhalb der Funktion suchen Sie zunächst in der angegebenen Spalte nach einer exakten Überstimmung mit der Eingabe. Wird ein passender Eintrag gefunden, wird die Funktion verlassen, nachdem der Index und der Text für das Steuerelement gesetzt wurden. Andernfalls wird nach einer Zeile gesucht, in der der Wert der angegebenen Spalte mit der Eingabe beginnt. Falls hier eine Übereinstimmung gefunden wird, wird der Eintrag ebenfalls markiert.

Function getIndex(intSpalte As Integer, objContr As ComboBox, strWert As String)
Dim lngZeile As Long
'Exakte Übereinstimmung suchen
For lngZeile = 0 To objContr.ListCount - 1
If UCase(Trim(strWert)) = UCase(Trim(objContr.Column(intSpalte, _
lngZeile))) Then
getIndex = lngZeile
objContr.Text = objContr.Column(1, lngZeile)
objContr.ListIndex = lngZeile
Exit Function
End If
Next lngZeile
'Anfang des Wertes suchen
For lngZeile = 0 To objContr.ListCount - 1
If InStr(1, UCase(Trim(objContr.Column(intSpalte, lngZeile))), _
UCase(Trim(strWert))) = 1 Then
getIndex = lngZeile
objContr.ListIndex = lngZeile
objContr.Text = objContr.Column(1, lngZeile)
Exit Function
End If
Next lngZeile
End Function

Funktion aufrufen

Nun müssen Sie natürlich noch dafür sorgen, dass die Funktion aufgerufen wird. Dazu benötigen Sie die Ereignisprozedur für das Ereignis NotInList. Damit das Ereignis eintritt, sind wiederum ein paar Einstellungen notwendig.

  1. Markieren Sie das Kombinationslistenfeld in der Entwurfsansicht des Formulars.

  2. Aktivieren Sie im Eigenschaftenfenster die Registerkarte Daten.

  3. Setzen Sie die Eigenschaft Nur Listeneinträge auf Ja.

  4. Erstellen Sie nun auf der Registerkate Ereignis eine Ereignisprozedur für das Ereignis Bei Nicht in Liste und ergänzen Sie sie wie in Listing 4.

Private Sub Typ_NotInList(NewData As String, Response As Integer)
getIndex 2, Me.Typ, NewData
Response = acDataErrContinue
End Sub

Als Werte übergeben Sie die Nummer der zu durchsuchenden Spalte (hier 2), das Steuerelement, in dessen Ereignisprozedur der Aufruf stattfindet, und den Parameter NewData der Ereignisprozedur.

Bild 2: Der gefundene Eintrag wird markiert, die Liste bleibt geöffnet.

Gibt der Benutzer nun einen Wert ein, der nicht in der erste Spalte gefunden wird, und drückt [Enter] oder [Tab], tritt das Ereignis NotInList ein. Die Funktion getIndex durchsucht nun die zweite Spalte nach einer Übereinstimmung. Kann sie gefunden werden, wird der Eintrag in der Liste markiert (Bild 2). Die Liste wird jedoch nicht geschlossen. Das hat den Vorteil, dass der Benutzer die Auswahl noch einmal prüfen und gegebenenfalls korrigieren kann. Durch erneutes Drücken von [Enter] oder [Tab] wird die Eingabe übernommen.

Alle Spalten durchsuchen

Bei Listen mit sehr vielen Spalten können Sie dem Benutzer noch mehr Komfort bieten, indem Sie ihm die Möglichkeit geben, alle Spalten zu durchsuchen. Dazu müssen Sie beim Abgleich zwischen dem eingegebenen Text und dem Inhalt der Spalte nur alle Spalten nacheinander abarbeiten. Dafür müssen Sie in diesem Fall auch keine Spaltennummer an die Funktion übergeben. Der Aufruf der Funktion erfolgt analog zur vorherigen, nur dass Sie hier keine Spaltennummer als Parameter übergeben.

Function getIndex_allespalten(objContr As ComboBox, strWert As String)
Dim lngZeile As Long
Dim lngSpalte As Long
For lngSpalte = 2 To objContr.ColumnCount
'Exakte übereinstimmung suchen
For lngZeile = 0 To objContr.ListCount - 1
If UCase(Trim(strWert)) = UCase(Trim(objContr.Column(lngSpalte, _
lngZeile))) Then
getIndex_allespalten = lngZeile
objContr.Text = objContr.Column(1, lngZeile)
objContr.ListIndex = lngZeile
Exit Function
End If
Next lngZeile
'Anfang des Wertes suchen
For lngZeile = 0 To objContr.ListCount - 1
If InStr(1, UCase(Trim(objContr.Column(lngSpalte, lngZeile))), _
UCase(Trim(strWert))) = 1 Then
getIndex_allespalten = lngZeile
objContr.ListIndex = lngZeile
objContr.Text = objContr.Column(1, lngZeile)
Exit Function
End If
Next lngZeile
Next lngSpalte
End Function

Private Sub Typ_NotInList(NewData As String, Response As Integer)
getIndex_allespalten Me.Typ, NewData
Response = acDataErrContinue
End Sub