Autocomplete in Textfeldern

Autocomplete kennen Sie vermutlich von Kombinationsfeldern. Hier können Sie einen oder mehrere Buchstaben eingeben und das Kombinationsfeld zeigt gleich den nächsten Eintrag der Datensatzherkunft an, der mit diesen Anfangsbuchstaben beginnt. Dieses Verhalten wollen wir auch gern für Textfelder programmieren. Dazu starten wir mit einer etwas einfacheren Variante, die Sie vielleicht vom VBA-Editor kennen: Wenn Sie dort beginnen, einen Objekt- oder Variablennamen zu schreiben, können Sie mit der Tastenkombination Strg + Leertaste dafür sorgen, dass IntelliSense aktiviert wird und passende Einträge anzeigt. Wir wollen dafür sorgen, dass an dieser Stelle einfach der erste passende Eintrag erscheint – wenn wir eine Liste der verfügbaren Einträge wollten, könnten wir ja direkt ein Kombinationsfeld verwenden.

Autocomplete per Tastenkombination

Im VBA-Editor verwendet man die Tastenkombination Strg + Leertaste, um beispielsweise Klassen oder Variablen anzuzeigen, die mit den bisher eingetippten Buchstaben beginnen. Wenn Sie etwa im Direktfenster die Buchstaben Curr eingeben und dann diese Tastenkombination verwenden, erscheint die Liste aus Bild 1 zur Auswahl.

Autocomplete per Intellisense im VBA-Editor

Bild 1: Autocomplete per Intellisense im VBA-Editor

Was für ein Verhalten wollen wir bei dieser ersten Technik erhalten und welche Voraussetzungen müssen dafür erfüllt sein Die Voraussetzung ist, dass das Textfeld an ein Feld einer Tabelle gebunden ist, aus dem wir die möglichen zu ergänzenden Texte gewinnen können.

Dann wollen wir dem Benutzer ermöglichen, einen oder mehrere Buchstaben einzugeben und dann mit der Tastenkombination Strg + Leertaste den nächsten passenden Eintrag zu ergänzen – so, dass die ergänzten Buchstaben markiert sind. Wenn wir also etwa in ein Textfeld, das an das Feld Artikelname der Tabelle tblArtikel gebunden ist, die Zeichenkette Ch eingeben und dann Strg + Leertaste betätigen, soll die Zeichenkette zu Chai ergänzt werden, wobei die ergänzten Buchstaben markiert sein sollen. Die Einfügemarke soll sich dabei hinter dem zuletzt eingegebenen Buchstaben befinden und vor dem ersten Buchstaben der Markierung (siehe Bild 2).

Autocomplete im Textfeld

Bild 2: Autocomplete im Textfeld

Das weitere Verhalten hängt davon ab, was der Benutzer als nächstes macht. Hier ist die Auswahl der möglichen Schritte:

  • Verlassen des Textfeldes: aktueller Text inklusive markiertem, vorgeschlagenem Text wird behalten
  • Betätigen der Nach links– oder Nach rechts-Taste beziehungsweise der Tasten Pos1 oder Ende: aktueller Text wird behalten, Markierung wird aufgehoben
  • Betätigen der Zurück-Taste: Markierter Text wird gelöscht.
  • Eingabe eines anderen Zeichens als des ersten Zeichens der Markierung: Suche nach einem passenden Eintrag und Anzeige der ergänzenden Zeichen mit Markierung
  • Eingabe des nächsten Zeichens der Markierung: Zeichen wird übernommen, die restlichen Zeichen bleiben markiert

Tastenkombination abfangen

Die erste Aufgabe ist, die Tastenkombination Strg + Leertaste abzufangen. Wir schauen in einem Kombinationsfeld, wann die Ergänzung stattfindet – beim Niederdrücken der Taste oder beim Loslassen der Taste. Das Ergebnis ist: Die Ergänzung erfolgt bereits beim Herunterdrücken der Taste. Also verwenden wir das Ereignis Bei Taste ab des Textfeldes, das wir in unserem Beispiel txtArtikelname genannt haben.

Dazu wählen wir im Eigenschaftenblatt für das Ereignis Bei Taste ab den Eintrag [Ereignisprozedur] aus und klicken dann auf die Schaltfläche mit den drei Punkten () neben dem Eigenschaftsfeld.

Dies legt im Klassenmodul des Formulars die folgende leere Prozedur an:

Private Sub txtArtikelname_KeyDown(KeyCode As Integer,  Shift As Integer)
End Sub

Um zu ermitteln, welche Werte die Parameter KeyCode und Shift beim Betätigen der Tastenkombination Strg + Leertaste liefern, geben wir diese innerhalb der Prozedur mit der folgenden Anweisung im Direktfenster aus:

Debug.Print KeyCode, Shift

Danach wechseln wir in die Formularansicht, setzen den Fokus in das Textfeld txtArtikel und geben die Tastenkombination Strg + Leertaste ein. Das Ergebnis ist, dass KeyCode den Wert 32 und Shift den Wert 2 liefert (32 entspricht auch der Konstanten vbKeySpace, 2 der Konstanten acCtrlMask).

Damit können wir schon eine Bedingung in das Ereignis einbauen, unter der wir aktiv werden wollen:

Private Sub txtArtikelname_KeyDown(KeyCode As Integer,  Shift As Integer)
     If KeyCode = vbKeySpace And Shift = acCtrlMask Then
         MsgBox "Action!"
     End If
End Sub

Danach gehen wir an die Umsetzung der ersten Aufgabe. Das Ergebnis sieht wie in Listing 1 aus. Die Prozedur verwendet drei Variablen. intPosStart speichert die Position der Eingabemarke zwischen und strTreffer eventuell gefundene Treffer. strText speichert den beim Betätigen der Taste im Textfeld vorhandenen Text.

Private Sub txtArtikelname_KeyDown(KeyCode As Integer, Shift As Integer)
     Dim intSelStart As Integer
     Dim strTreffer As String
     Dim strText As String
     If KeyCode = vbKeySpace And Shift = acCtrlMask Then
         intSelStart = Me!txtArtikelname.SelStart
         strText = Me!txtArtikelname.Text
         strTreffer = Nz(DLookup("Artikelname", "tblArtikel", "Artikelname LIKE ''" & strText & "*''"), strText)
         Me!txtArtikelname.Text = strTreffer
         Me!txtArtikelname.SelStart = intSelStart
         Me!txtArtikelname.SelLength = 255
         KeyCode = 0
     End If
End Sub

Listing 1: Automatisches Ergänzen bei Strg + Leertaste

Nach dem Prüfen der Tastenkombination speichert die Prozedur die aktuelle Position der Einfügemarke in der Variablen intSelStart und den aktuellen Text in strText. Dann sucht sie mit der DLookup-Funktion in der zugrunde liegenden Tabelle nach einem Wert, der mit den Zeichen aus strText beginnt.

Findet sie einen solchen Wert, wird dieser in strTreffer geschrieben, sonst landet der zweite Parameter der Nz-Funktion als Ergebnis in strTreffer – in diesem Fall der vorherige Inhalt des Textfeldes aus strText.

Danach folgt die Aktualisierung des Textfeldes. Dabei weisen wir der Eigenschaft Text den Inhalt von strTreffer zu und setzen die Eingabemarke über die Eigenschaft SelStart auf die vorherige Position, also intSelStart. Außerdem stellen wir die Eigenschaft SelLength auf den Wert 255 ein, was der größten Zeichenkette entspricht, die wir in dieses Textfeld eingeben können. Damit markieren wir schlicht den Text von der Position der Einfügemarke aus bis zum Schluss.

Danach folgt noch eine wichtige Anweisung (KeyCode = 0). Diese sorgt dafür, dass die eingegebene Taste nicht an das Textfeld geschickt wird. Ohne diese Anweisung würde die Prozedur zwar wie gewünscht funktionieren, aber durch das anschließende Senden des Leerzeichens an das Textfeld würde der markierte Bereich wieder gelöscht und durch das Leerzeichen überschrieben werden.

Damit haben wir aber nur den Beginn gemacht. Nun kommen wir an den Punkt, wo der Benutzer das nächste Zeichen eingibt, eines löscht, die Einfügemarke mit den Tasten Nach links, Nach rechts, Pos1 oder Ende bewegt oder das Feld verlässt.

Einschränkung: Einfügemarke nicht hinter dem letzten Zeichen

An dieser Stelle wollen wir uns bereits um eine kleine Einschränkung kümmern: Der zuvor beschriebene Ablauf soll nicht durchgeführt werden, wenn der Benutzer die Tastenkombination Strg + Leerzeichen betätigt, während die Einfügemarke sich nicht am Ende des bisher eingegebenen Textes befindet. Wir müssen also noch eine Prüfung der Position der Einfügemarke bezogen auf den vorhandenen Text hinzufügen. Diese fügen wir hinter den beiden Anweisungen zur Ermittlung der Startposition der Markierung und des Textes ein und prüfen, ob intSelStart genau der Länge des eingegebenen Textes entspricht – was bedeutet, dass die Einfügemarke sich hinter dem letzten Zeichen befindet:

---
strText = Me!txtArtikelname.Text
If intSelStart = Len(strText) Then
     ...
End If
KeyCode = 0
...

Verlassen des Feldes mit Autocomplete

Wenn der Benutzer mit Strg + Leertaste Autocomplete aktiviert und gegebenenfalls eine Ergänzung hinzugefügt hat und dann das Feld beispielsweise mit der Tabulator-Taste oder durch einen Mausklick auf ein anderes Steuer-element verlässt, soll der enthaltene Text inklusive des markierten Teils beibehalten werden.

Für dieses Verhalten brauchen wir gar nichts zu tun – es ist bereits implementiert.

Bewegen der Einfügemarke

Die Einfügemarke kann der Benutzer bei markiertem Text auf verschiedene Arten bewegen – zum Beispiel auf die folgenden:

  • Nach links-Taste: Bewegt die Einfügemarke um eine Position nach links.
  • Nach rechts-Taste: Bewegt die Einfügemarke um eine Position nach rechts.
  • Pos1-Taste: Bewegt die Einfügemarke an die erste Position.
  • Ende-Taste: Bewegt die Einfügemarke an die letzte Position.

Wichtig ist an dieser Stelle, dass diese Bewegungen keine Änderungen am Text vornehmen.

Wir wollen das Verhalten wie bei der Autocomplete-Funktion von Kombinationsfeldern abbilden. Dort wird bei Betätigung dieser Tasten die übliche Funktion ausgeführt und die Markierung im Text wird entfernt.

Und auch hier bekommen wir das gewünschte Verhalten wieder frei Haus geliefert – es ist bereits implementiert.

Betätigen der Zurück- und der Entf-Taste

Sie ahnen es bereits: Auch hier brauchen Sie nichts zu tun. Wenn Sie die Zurück– oder die Entf-Taste betätigen, während der hintere Teil des Textes markiert ist, wird der markierte Text einfach gelöscht.

Eingabe weiterer Zeichen

Nun wird es allerdings wieder interessant: Wenn der Benutzer ein weiteres Zeichen eingibt, gibt es zwei Möglichkeiten:

  • Das Zeichen entspricht dem ersten Zeichen der Markierung. Dann soll dieses Zeichen aus der Markierung herausgenommen werden und die übrigen in der Markierung enthaltenen Zeichen sollen weiterhin markiert bleiben.
  • Das Zeichen entspricht nicht dem ersten Zeichen der Markierung. Dann soll die Prozedur eine neue Suche starten, wobei der erste, nicht markierte Teil des Textes im Textfeld plus dem soeben eingegebenen Zeichen als Suchbegriff verwendet wird.

Wir schauen uns erst den Fall an, dass der Benutzer das erste Zeichen der Markierung eingegeben hat. In diesem Fall wollen wir wissen, welches Zeichen der Benutzer eingegeben hat und dieses dann mit dem ersten Zeichen der Markierung vergleichen.

KeyCode, Shift und Ascii

Das ist mit den Parametern KeyCode und Shift, die wir von der Ereignisprozedur geliefert bekommen, nicht so einfach – zumindest dann nicht, wenn es sich bei den Zeichen nicht um Buchstaben oder Zahlen handelt. Wenn Sie beispielsweise ein kleines a eingeben, liefert KeyCode den Wert 65. Wenn Sie ein großes A eingeben, also Umschalt + a, liefert KeyCode ebenfalls den Wert 65. Zusätzlich liefert Shift den Wert 1, also acShiftMask. Der Ascii-Wert für das große A ist jedoch 65, für das kleine a lautet er 97. Die Ascii-Werte für Zeichen ermitteln Sie mit der Asc-Funktion:

Debug.Print Asc("a")
97

Bei den Buchstaben ist das kein Problem: Wir könnten dann in einer kleinen Funktion prüfen, ob Shift den Wert acShiftMask liefert. Ist das der Fall, entspricht der Wert von KeyCode dem Ascii-Wert 65. Ist Shift nicht gleich acShiftMask, müssen wir zum Wert von KeyCode die Zahl 32 hinzu addieren.

Das funktioniert so für alle Buchstaben. Dann schauen wir uns weitere gängige Zeichen an, die in Textfeldern verwendet werden – zum Beispiel das Minus-Zeichen, das gern als Bindestrich verwendet wird. Die Asc-Funktion liefert dafür:

Debug.Print Asc("-")
  45 

KeyCode liefert allerdings den Wert 189. Damit würde die Funktion zum Übersetzen von KeyCode in Ascii schon umfangreicher werden. Genau genommen müssten wir damit alle Zeichen übersetzen, da wir ja nie wissen, welche Zeichen der Benutzer benötigt.

Allerdings gibt es noch eine Alternative: die Ereignisprozedur Bei Taste. Die sieht im leeren Zustand für unser Textfeld txtArtikelname wie folgt aus:

Private Sub txtArtikelname_KeyPress(KeyAscii As Integer)
     Debug.Print KeyAscii
End Sub

Der hier verwendete Parameter KeyAscii liefert genau die gleichen Werte, die auch die Asc-Funktion zurückgibt. Damit könnten wir also für die weiterführende Eingabe von Zeichen arbeiten.

Zeichen mit dem Bei Taste-Ereignis untersuchen

Aber wenn wir nun eine weitere Ereignisprozedur hinzunehmen, um die Eingabe regulärer Zeichen zu untersuchen – macht es dann Sinn, die Prüfung auf Strg + Leerzeichen in der Ereignisprozedur Bei Taste ab zu belassen Und kommen sich die beiden Prozeduren dann nicht ins Gehege, weil die Bei Taste-Ereignisprozedur auch nochmal auf Strg + Leertaste reagiert

Ja, es macht Sinn, denn die beiden Ereignisprozeduren kommen sich bei Eingabe der Tastenkombination Strg + Leertaste nicht ins Gehege. Genau genommen wird das Ereignis Bei Taste durch diese Tastenkombination überhaupt nicht ausgelöst. Also arbeiten wir zunächst mit dem Ereignis Bei Taste weiter.

Die entsprechende Ereignisprozedur füllen wir wie folgt:

Private Sub txtArtikelname_KeyPress(KeyAscii As Integer)
     Dim intSelStart As Integer
     Dim intSelLength As Integer
     Dim strText As String
     Dim strNaechster As String
     intSelStart = Me!txtArtikelname.SelStart
     intSelLength = Me!txtArtikelname.SelLength
     strText = Me!txtArtikelname.Text
     If intSelLength > 0 Then
         strNaechster = Mid(strText, intSelStart + 1, 1)
         If KeyAscii = Asc(strNaechster) Then
             With Me!txtArtikelname
                 .Text = strText
                 .SelStart = intSelStart + 1
                 .SelLength = 255
             End With
         End If
     End If
End Sub

Der Plan ist prinzipiell gut: Wir erfassen wieder die Position der Markierung und die Länge der Markierung sowie den gesamten Text. Dann prüfen wir, ob es eine Markierung mit mehr als 0 Zeichen gibt. Ist das der Fall, ermitteln wir den ersten Buchstaben der Markierung und tragen diesen in die Variable strNaechster ein.

Wenn KeyAscii dem Ascii-Wert von strNaechster entspricht, hat der Benutzer genau das nächste Zeichen eingegeben. Dann schreiben wir den Text aus strText zurück in das Textfeld, lassen die Markierung gegenüber der vorherigen Markierung um ein Zeichen weiter hinten beginnen. Außerdem soll die Markierung auch wieder bis zum Ende der Zeichenkette gehen.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar