Textfelder sind keine übermäßig flexiblen Eingabesteuerelemente. Man gibt Text ein, löscht oder bearbeitet diesen oder kopiert Inhalte beziehungsweise fügt diese ein. Einige Anwendungen aber liefern für Textfelder überraschenden Komfort: Diese zeigen beispielsweise den zuletzt eingegebenen passenden Wert an, sobald man ein oder mehrere Zeichen in das Textfeld eingegeben hat. Der Benutzer kann dann per Tab- oder Eingabetaste die aktuell angezeigte Auswahl einfach übernehmen. Dies schauen wir uns genauer an und bauen eine passende Funktion für Access-Textfelder nach.
Was benötigen wir also, um diese Funktion nachzubilden Zunächst einmal ein Textfeld, das wir damit ausstatten wollen.
Wir konzentrieren uns zunächst auf ein einzelnes Textfeld und bauen anschließend eine Klasse, mit der wir die Funktion ganz einfach mit wenigen Codezeilen auf beliebige Textfelder übertragen können.
Außerdem brauchen wir eine Tabelle, welche die zuletzt in ein Feld eingegebenen Daten speichert. Diese könnte im Entwurf beispielsweise wie in Bild 1 aussehen.
Bild 1: Erster Entwurf der Tabelle zum Speichern der zuletzt verwendeten Werte
Hier finden Sie neben dem Primärschlüsselwert drei Felder, die den eigentlichen Wert, den Namen des Formulars sowie den Namen des Steuerelements aufnehmen, dessen Inhalte gespeichert und wieder zur Verfügung gestellt werden sollen.
Dazu bauen wir gleich einen eindeutigen Index über die drei Felder LetzterWert, Formularname und Steuerelementname ein, der dafür sorgt, dass jeder Wert nur einmal je Formular und Steuerelement gespeichert wird.
Fürs Erste ist dies ein sinnvoller Ansatz. Wenn man jedoch ein wenig weiterdenkt, fällt auf, dass ein Feld ja möglicherweise nicht nur in einem einzigen Formular abgebildet wird, sondern vielleicht auch noch in weiteren Formularen. Es scheint also keine gute Idee zu sein, die Speicherung der zuletzt verwendeten Werte für ein Feld auf Formularname und Steuerelementname zu beziehen, sondern eher auf den reinen Feldnamen.
Woher bekommen wir den üblicherweise aus der Eigenschaft Steuerelementinhalt des Textfeldes. Dies gilt aber auch nur für solche Textfelder, die an ein Feld einer Tabelle gebunden sind.
Aber wollen wir uns nur auf gebundene Felder beschränken Was ist, wenn wir etwa ein Feld zur Eingabe eines Filterkriteriums mit der Funktion zur Anzeige der zuletzt verwendeten Werte nutzen möchten Dieses ist ja normalerweise nicht an ein Feld einer Tabelle gebunden.
Also müssen wir uns ein anderes Merkmal aussuchen, mit dem wir die betroffenen Steuerelemente markieren. Dafür gibt es jedoch einen passenden Kandidaten: die Eigenschaft Marke, unter VBA mit der Eigenschaft Tag ansprechbar.
Wir hinterlegen also für alle Felder, welche die gleichen zuletzt verwendeten Werte anzeigen sollen, einen entsprechenden Bezeichner in der Marke-Eigenschaft.
Die Tabelle tblLetzteWerte ändern wir entsprechend, indem wir die beiden Felder Formularname und Steuerelementname entfernen und dafür das Feld LetzterWertMarke einfügen (s. Bild 2).
Bild 2: Zweiter Entwurf der Tabelle zum Speichern der zuletzt verwendeten Werte
Zuletzt eingegebene Werte speichern
Nun müssen wir uns darum kümmern, dass überhaupt Werte in diese Tabelle gelangen. Dazu legen Sie ein erstes Formular mit einem einzigen Textfeld an und nennen dieses txtBeispieltext. Im Eigenschaftsfenster stellen Sie außerdem die Eigenschaft Marke auf den Wert Beispielfeld ein (s. Bild 3).
Bild 3: Ausstatten des Textfeldes mit der Marke-Eigenschaft
Nun sollen vom Benutzer eingegebene Werte nach der Eingabe in der Tabelle tblLetzteWerte gespeichert werden. Dazu nutzen wir die Ereignisprozedur, die durch das Ereignis Nach Aktualisierung des Textfeldes ausgelöst wird. Wählen Sie hier den Wert [Ereignisprozedur] aus und klicken Sie dann auf die Schaltfläche mit den drei Punkten (…).
Es erscheint der VBA-Editor und zeigt die noch leere Prozedur txtLetzteWerte_AfterUpdate an. Diese soll nun zunächst einfach den aktuellen Wert des Textfeldes in die Tabelle tblLetzteWerte eintragen – samt dem verwendeten Wert für die Eigenschaft Marke.
Die Prozedur sieht nun wie in Listing 1 aus und verwendet eine INSERT INTO-SQL-Abfrage, um den Datensatz in der Tabelle zu speichern.
Private Sub txtBeispieltext_AfterUpdate() Dim db As DAO.Database Set db = CurrentDb db.Execute "INSERT INTO tblLetzteWerte(LetzterWert, LetzterWertMarke) VALUES(''" _ & Me!txtBeispieltext & "'', ''" _ & Me!txtBeispieltext.Tag & "'')", dbFailOnError End Sub
Listing 1: Speichern des aktuellen Wertes des Textfeldes txtBeispieltext in der Tabelle tblLetzteWerte
Dummerweise funktioniert diese Methode nur, solange Sie keinen Wert ein zweites Mal in die Tabelle eintragen. Dann schlägt nämlich die weiter oben vorgenommene Restriktion zu, nach der jede Kombination aus dem Wert Marke und dem Inhalt des Textfeldes nur einmal in der Tabelle gespeichert werden darf (s. Bild 4). Wie kommen wir aus diesem Dilemma heraus
Bild 4: Fehler beim erneuten Hinzufügen der gleichen Kombination aus Marke und Wert
Dazu gibt es gleich mehrere Möglichkeiten. Die erste ist die folgende: Wir deaktivieren vorher die Fehlerbehandlung mit On Error Resume Next. Dies unterbindet schlicht die Fehlermeldung. Der einzufügende Datensatz ist schließlich schon vorhanden, warum diesen also nochmals einfügen
An dieser Stelle müssen wir uns überlegen, wie und vor allem in welcher Reihenfolge die zuletzt verwendeten Werte im Textfeld angezeigt werden sollen. Benötigen wir eine alphabetische Reihenfolge Wollen wir die Werte in der Reihenfolge der Eingabe anzeigen Oder sollen sogar die am häufigsten verwendeten Werte bevorzugt angezeigt werden
Die Antwort auf diese Frage hängt von mehreren Faktoren ab. Der erste ist: Soll der passende Wert einfach nur als Auto-Ergänzung im Textfeld angezeigt werden – also etwa so wie in Bild 5
Bild 5: Automatische Ergänzung des eingegebenen Ausdrucks
Oder sollen diese in einer Art Kombinationsfeld oder gar in einer Kombination aus Textfeld und Listenfeld erscheinen In beiden Fällen sollte es eigentlich konfigurierbar sein, ob die Werte in der Reihenfolge der letzten Eingabe erscheinen oder in alphabetischer Reihenfolge.
Die Abfrage der Werte in alphabetischer Reihenfolge ist kein Problem: Wir können die dazu verwendete Abfrage ja einfach entsprechend sortieren. Wie aber sieht es aus, wenn die zuletzt eingegebenen Werte eine höhere Priorität haben sollen In diesem Fall gibt es wieder zwei Varianten: Erstens können wir die Zeit der letzten Eingabe in einem weiteren Feld der Tabelle speichern. Zweitens können wir das Primärschlüsselfeld nutzen, das ja mit einem Autowert gefüllt wird. Je höher der Wert im Primärschlüsselfeld, desto neuer ist der eingegebene Wert.
Dies ziehen wir dem Hinzufügen eines eigenen Datumsfeldes vor, müssen dafür aber die Prozedur zum Hinzufügen des aktuell eingegebenen Eintrags etwas anpassen: Dies soll nämlich nun einen eventuell bereits vorhandenen Eintrag mit dem gleichen Wert löschen, bevor der neue Wert angelegt wird.
Dadurch erhält der zuletzt angelegte Wert automatisch den höchsten Wert, auch wenn er zuvor bereits einmal angelegt wurde.
Dies sieht dann beispielsweise wie in Bild 6 aus: Der Wert Erster Wert wird nach der ersten Eingabe erneut eingegeben. Die Prozedur löscht dann den ersten Wert und legt automatisch einen neuen Eintrag mit identischem Wert an. Die Prozedur sieht nun wie in Listing 2 aus.
Bild 6: Bei Eingabe eines bereits vorhandenen Werts wird der alte Wert gelöscht.
Zuletzt verwendete Werte abrufen
Die Voraussetzungen für die Anzeige der zuletzt verwendeten Werte sind somit geschaffen. Nun schauen wir uns die verschiedenen Varianten an, um diese Werte anzuzeigen.
Im ersten Ansatz soll einfach nur der naheliegendste Wert bezogen auf die bis dahin eingegebenen Zeichen angezeigt werden – und zwar so, dass die bereits eingegebenen Zeichen nicht markiert und die zu ergänzenden Zeichen markiert erscheinen.
Dazu benötigen wir die zuletzt eingegebenen Werte. Aber wie viele Und wie viele Werte sollen überhaupt gespeichert werden Zehn, hundert, tausend oder gar noch mehr Machen wir uns doch um die Anzahl zunächst keinen Kopf und verwenden einfach alle zuletzt eingelesenen Werte für das Textfeld mit dem entsprechenden Wert der Eigenschaft Marke. Diese lesen wir gleich beim öffnen des Formulars in ein Array rstLetzteWerte ein, das wir auch modulweit verfügbar machen wollen. Also deklarieren wir dieses wie folgt im Kopf des Klassenmoduls des Formulars:
Private strLetzteWerte() As String
Die Funktion zum Einlesen der zuletzt verwendeten Werte soll diese allerdings nicht in der Reihenfolge der Verwendung liefern, sondern einfach in der alphabetischen Reihenfolge. Die Funktion heißt LetzteWerteEinlesen und sieht wie in Listing 3 aus. Sie erwartet den Wert der Tag-Eigenschaft des betroffenen Textfeldes und füllt die modulweit deklarierte Variable strLetzteWerte mit den entsprechenden Werten.
Private Function LetzteWerteEinlesen(strTag As String) As Long Dim db As DAO.Database Dim rst As DAO.Recordset Dim i As Integer Set db = CurrentDb Set rst = db.OpenRecordset("SELECT LetzterWert FROM tblLetzteWerte " _ & "WHERE LetzterWertMarke = ''" & strTag _ & "'' ORDER BY LetzterWert", dbOpenDynaset) Do While Not rst.EOF ReDim Preserve strLetzteWerte(i) As String strLetzteWerte(i) = rst!LetzterWert i = i + 1 rst.MoveNext Loop LetzteWerteEinlesen = i End Function
Listing 3: Diese Funktion liest die zuletzt verwendeten Werte für das angegebene Steuerelement ein.
Diese bezieht sie natürlich aus der Tabelle tblLetzteWerte. Auf diese greift sie über ein Recordset zu, das alle Datensätze dieser Tabelle liefert, deren Feld LetzterWertMarke dem mit strTag übergebenen Wert entspricht. Die Funktion durchläuft alle Datensätze dieses Recordsets in einer Do While-Schleife und erledigt darin drei Schritte:
- das Vergrößern des Arrays auf den aktuell in der Variablen i gespeicherten Wert,
- das Eintragen des Wertes des Feldes LetzterWert des aktuellen Datensatzes in das Element des Arrays strLetzteWerte mit dem Index i und
- das Erhöhen des Index i um den Wert 1.
Diese Funktion wird gleich beim Laden des Formulars ausgelöst:
Private Sub Form_Load() LetzteWerteEinlesen Me!txtBeispieltext.Tag End Sub
Ergänzen des aktuellen Ausdrucks
Nun kommt der interessante Teil: Wir wollen dafür sorgen, dass beim Eingeben eines oder mehrerer Zeichen in das Textfeld ein passender Wert aus dem Array strLetzteWerte gefunden wird und die fehlenden Zeichen hinten an den bereits eingegebenen Ausdruck anhängen und als markiert kennzeichnen.
Den ersten Teil der Prozedur, die durch das Ereignis Bei Taste auf des Textfeldes ausgelöst wird, finden Sie in Listing 4. Die Prozdur prüft zunächst den Wert des Parameters KeyCode, der einen dem zuletzt eingegebenen Zeichen entsprechenden Zahlenwert enthält.
Private Sub txtBeispieltext_KeyUp(KeyCode As Integer, Shift As Integer) Dim strAktuellerWert As String Dim intSelStart As Integer Dim intSelLength As Integer Dim strFeldTemp As String Dim intStarttemp As String Dim intLenTemp As Integer Dim i As Integer Select Case KeyCode Case vbKeyTab, vbKeyLeft, vbKeyRight Exit Sub Case vbKeyBack Case Else strAktuellerWert = Me!txtBeispieltext.Text intSelStart = Me!txtBeispieltext.SelStart intSelLength = Me!txtBeispieltext.SelLength End Select
Listing 4: Diese Ereignisprozedur ergänzt den aktuellen Ausdruck (Teil I)
Der besseren Lesbarkeit halber haben wir diese in der Select Case-Bedingung, die diese prüft, durch die entsprechenden Konstanten ersetzt, also etwa 9 durch vbKeyTab, 37 durch vbKeyLeft, 39 durch vbKeyRight und 8 durch vbKeyBack.
Wenn der Benutzer die Tabulatortaste, die Nach Links– oder die Nach Rechts-Taste verwendet, soll die Prozedur verlassen werden. Im Falle der Tabulatortaste verlässt der Fokus das Textfeld, im Falle der Nach Links– und der Nach Rechts-Taste springt der Cursor einfach nach links oder rechts. Eine eventuell bereits eingeblendete Erweiterung des aktuellen Ausdrucks bleibt somit erhalten.
Sollte der Benutzer die Zurück-Taste betätigt haben, wird diese ebenfalls einfach ausgeführt – aber die Prozedur wird nicht verändert. Genau wie die Eingabe eines Zeichens soll auch das Löschen eines Zeichens dazu führen, dass die Prozedur nach passenden Werten sucht.
Sollte der Benutzer ein beliebiges Zeichen mit Ausnahme der soeben erwähnten und in den ersten beiden Zweigen der Select Case-Bedingung abgearbeiteten eingegeben haben, tritt der dritte Zweig der Select Case-Bedingung in Aktion. Dort führt die Prozedur folgende Aktionen aus:
- Speichern des aktuell im Textfeld angezeigten Wertes in der Variablen strAktuellerWert,
- der aktuellen Cursorposition in der Variablen intSelStart und
- der Länge der aktuellen Markierung in der Variablen intSelLength.
Damit geht es nun weiter mit Listing 5. Hier prüft die Prozedur nun, ob überhaupt schon ein Zeichen eingegeben wurde. Ist dies nicht der Fall, endet die Prozedur hier. Anderenfalls stellt sie die Variable intStarttemp auf den Wert 1 ein. Damit ermittelt die Prozedur nun mithilfe der Mid-Funktion und den Werten der beiden Variablen intSelStart und intSelLength den aktuellen Inhalt des Textfeldes und trägt diesen in die Variable strFeldTemp ein. Die Länge dieser Zeichenkette landet anschließend in der Variablen intLenTemp. Ist diese größer als 0, prüft die Prozedur mit der Hilfsfunktion ArrayGefuellt, ob das Array strLetzteWerte überhaupt einen Wert enthält (s. Listing 6).
Private Function ArrayGefuellt(var() As String) As Boolean Dim intTest As Integer If IsArray(var) Then On Error Resume Next intTest = LBound(var()) If Err.Number = 0 Then ArrayGefuellt = True Else ArrayGefuellt = False End If Else ArrayGefuellt = False End If End Function
Listing 5: Diese Funktion prüft, ob ein Array gefüllt ist.
Ist dies nicht der Fall, soll dieses Array zunächst mit der Funktion LetzteWerteEinlesen gefüllt werden. Bei diesem Aufruf verwerten wir auch den Rückgabewert der Funktion. Diesen vergleicht die Prozedur mit dem Wert 0. Ist der Wert größer, enthält das neu gefüllte Array mindestens einen Eintrag und wir können prüfen, ob der aktuelle Wert des Textfeldes mit einem der Werte des Arrays übereinstimmt. Anderenfalls wird die Prozedur an dieser Stelle beendet.
Nun starten wir in einer For…Next-Schleife den Vergleich des aktuellen Ausdrucks aus strFeldTemp mit den ersten Zeichen der in strLetzteWerte gespeicherten Zeichenketten. Wenn wir also die Zeichen Ers eingegeben haben, vergleicht die Prozedur diese Zeichenkette jeweils mit den ersten drei Zeichen der Werte aus strLetzteWerte. Stimmen diese überein, prüft eine weitere If…Then-Bedingung, ob die in intSelStart gespeicherte Position der Einfügemarke mit der Länge der Zeichenkette aus strAktuellerWert übereinstimmt. Ist dies der Fall, prüft die Prozedur lediglich noch, ob der Benutzer die Zurück-Taste betätigt hat (vbKeyBack). In diesem Fall wird der Wert für die aktuelle Markierung um eine Position nach links verschoben.
Anderenfalls stellt die Prozedur den Inhalt des Textfeldes über die Eigenschaft Text auf die bisher eingegebenen Zeichen (also etwa Ers) plus die folgenden Zeichen des gefundenen Ausdrucks aus dem Array strLetzteWerte ein (beispielsweise ter Wert). Beides ergibt also nun zusammen Erster Wert. Warum tragen wir also nicht direkt den kompletten Wert aus dem Array strLetzterWert ein Weil dies auch die Groß-/Kleinschreibung ändern könnte. Vielleicht hat der Benutzer ja die Zeichen ers eingegeben, was dann in Ers geändert werden würde.
Schließlich stellt die Prozedur die beiden Eigenschaften SelStart und SelLength so ein, dass alle Zeichen, die automatisch ergänzt wurden, markiert werden – in diesem Fall also die Zeichen ter Wert von Erster Wert. Den Start der Markierung entnimmt die Prozedur dabei der Variablen intSelStart, die Länge der Markierung berechnet sie aus der Länge der Zeichenkette aus dem Array strLetzteWerte und der Anzahl der bisher eingegebenen Zeichen.
AfterUpdate verhindern
Damit wäre prinzipiell alles erledigt – wenn die Zuweisung eines Wertes an die Eigenschaft Text nicht ungünstigerweise das Ereignis Nach Aktualisierung des Textfeldes auslösen würde. Und diese sorgt ja gerade dafür, dass die zuletzt eingegebenen Werte in der Tabelle tblLetzteWerte erscheinen. Wenn diese aber nun jedes Mal ausgelöst wird, wenn der Benutzer ein neues Zeichen eingegeben hat, für das im Array strLetzteWerte ein Wert gefunden wurde, ist die Tabelle tblLetzteWerte schnell voller unsinniger Werte.
Da wir nicht verhindern können, dass das Ereignis Nach Aktualisierung im genannten Fall ausgelöst wird, müssen wir dafür sorgen, dass die enthaltenen Anweisungen nur ausgeführt werden, wenn dies sinnvoll ist.
Dazu verwenden wir eine modulweit deklarierte Variable namens bolKey:
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