Selektieren in langen Texten

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Wenn Sie einen Text in einem Textfeld selektieren wollen, erledigen Sie das in der Regel per Maus oder Tastatur. Das geht jedoch auch per Code – in der Regel durch das Setzen der Eigenschaften SelStart und SelLength. Damit lässt sich auch die Position der Markierung auslesen, wenn diese von Hand gesetzt wurde. Die genannten Eigenschaften haben jedoch einen Haken: Sie können damit nur in Texten mit bis zu 32.767 Zeichen arbeiten, da die Eigenschaften als Integer definiert sind. Abhilfe schafft die Windows API – alle notwendigen Informationen dazu finden Sie in diesem Beitrag.

Anlass für die Untersuchung dieses Problems ist die Lösung aus dem Beitrag Volltextsuche mit Fundstellen (www.access-im-unternehmen.de/1119), wo wir einen Suchbegriff an der entsprechenden Stelle im Text durch eine Markierung hervorheben wollen.

Die dort verwendeten Texte sind jedoch mitunter länger als 32.767 Zeichen, enthalten also mehr Zeichen, als durch den Datentyp Integer erfasst werden können.

Zum Setzen der Markierung wollten wir aber gern die beiden Eigenschaften SelStart und SelLength verwenden. Wenn wir in einem Textfeld mit einem langen Text eine Markierung setzen wollen, die hinter dem 32.767ten Zeichen liegt, lösen wir damit einen überlauf-Fehler aus.

Als Spielwiese für die nachfolgend entwickelte Lösung erstellen wir eine Tabelle namens tblTexte mit dem Primärschlüsselfeld TextID und dem Feld Inhalt mit dem Datentyp Langer Text beziehungsweise Memo (s. Bild 1).

Tabelle zum Speichern langer Texte

Bild 1: Tabelle zum Speichern langer Texte

Das Formular, mit dem wir das Selektieren ausprobieren wollen, sieht im Entwurf wie in Bild 2 aus. Es ist an die Tabelle tblTexte gebunden. Damit wir möglichst viel Text sehen, haben wir die Eigenschaften Horizontaler Anker und Vertikaler Anker für das Textfeld, das an das Feld Inhalt gebunden ist, auf Beide eingestellt. Das Textfeld haben wir außerdem txtInhalte benannt.

Entwurf des Testformulars

Bild 2: Entwurf des Testformulars

Oben finden Sie noch zwei Textfelder, in die wir die gewünschten Werte für SelStart und SelLength eintragen können und eine Schaltfläche namens cmdGo, welche die Markierung anwenden soll. Die Schaltfläche cmdGo soll die folgende Ereignisprozedur auslösen:

Private Sub cmdGo_Click()
     With Me!txtInhalt
         .SetFocus
         .SelStart = Me!txtSelStart
         .SelLength = Me!txtSelLength
     End With
End Sub

Wenn wir nun etwa die Werte 10 für SelStart und 20 für SelLength eingeben, erhalten wir im Textfeld eine Markierung wie in Bild 3.

Setzen einer Markierung am Anfang des Textes

Bild 3: Setzen einer Markierung am Anfang des Textes

Nun geben wir einmal einen Wert größer als 32.767 für die Eigenschaft SelStart ein und klicken auf die Schaltfläche cmdGo. Dies löst nun den Fehler aus Bild 4 aus. Die Ursache für das Problem können wir uns gleich im Direktbereich nochmal bestätigen lassen.

Fehler beim Einsatz von Werten größer als 32.767

Bild 4: Fehler beim Einsatz von Werten größer als 32.767

Dort geben wir eine Debug.Print-Anweisung ein, die mit der Funktion TypeName den Datentyp der SelStart-Eigenschaft liefert:

Debug.Print TypeName( Me!txtInhalt.SelStart)
     Integer

Alternative: API-Funktionen

An dieser Stelle können wir also wohl nicht ändern und müssen uns eine Alternative überlegen.

Diese finden wir in den API-Funktionen von Windows.

Damit lassen sich die meisten Dinge bezüglich Benutzeroberfläche und Steuer-elemente erledigen, die Sie auch über den Befehlssatz von Access/VBA einstellen können – allerdings sieht das auf diesem Wege immer ein wenig komplizierter aus.

Für unsere Anforderung nutzen wir den Code aus Listing 1. Hier definieren wir zunächst zwei API-Funktionen, um diese innerhalb des VBA-Codes zu nutzen. Die erste API-Funktion heißt SendMessage und wird für das Senden aller möglichen Nachrichten verwendet.

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
     ByVal wParam As Long, lParam As Any) As Long
Private Declare Function GetFocus Lib "user32" () As Long
Private Const EM_SETSEL  As Long = &HB1
Private Sub cmdGo_Click()
     Dim lngHandle As Long
     Dim lngSelStart As Long
     Dim lngSelLength As Long
     With Me!txtInhalt
         .SetFocus
         lngHandle = GetFocus
         lngSelStart = Me!txtSelStart
         lngSelLength = Me!txtSelLength
       SendMessage lngHandle, EM_SETSEL, lngSelStart, ByVal lngSelStart + lngSelLength
     End With
End Sub

Listing 1: API-Code zum Markieren eines Textabschnitts

Um anzugeben, um welche Art Nachricht es sich handelt, übergibt man mit dem zweiten Parameter eine Konstante für den Typ der Nachricht.

Der erste Parameter erwartet ein Handle auf das anzusprechende Element der Benutzeroberfläche, also eine Zahl, mit welcher das entsprechende Element referenziert wird. Die beiden folgenden Parameter nehmen je nach Aufgabe verschiedene Werte entgegen.

Die zweite API-Funktion, die wir nutzen wollen, heißt GetFocus und soll uns dabei unterstützen, das Handle für das anzusprechende Textfeld im Access-Formular zu ermitteln. Normalerweise haben Elemente wie Schaltflächen, Textfelder et cetera eine Eigenschaft namens Handle oder hWnd, um diesen Wert direkt zu ermitteln, aber unter Access läuft dies etwas anders.

Hier erhält nur das Steuer-element ein Handle, das aktuell den Fokus besitzt. Deshalb müssen wir erst den Fokus auf das Steuer-element einstellen und dann mit der API-Funktion GetFocus das entsprechende Handle ermitteln.

In der Prozedur cmdGo definieren wir nun zunächst entsprechende Variablen für das Handle und für die beiden zu übergebenen Parameter, also lngSelStart und lngSelLength. Dann setzen wir wie bereits im vorherigen Beispiel den Fokus auf das Textfeld und ermitteln mit der GetFocus-Funktion das Handle des Textfeldes, welches dann in der Variablen lngHandle landet.

Dann schreiben wir die Werte der beiden Textfelder txtSelStart und txtSelLength in die Variablen lngSelStart und lngSelLength.

Für den ersten Parameter der gleich aufzurufenden SendMessage-Funktion nutzen wir die Konstante EM_SETSEL. Damit haben wir nun alle Parameter für den Aufruf der API-Funktion SendMessage zusammen und können diese aufrufen.

Zu beachten ist hier noch, dass wir nicht die Länge der zu setzenden Markierung angeben, sondern die Position des ersten und des letzten Zeichens. Daher verwenden wir als vierten Parameter lngSelStart + lngSelLength. Das Ergebnis überzeugt – wir können damit auch Bereiche markieren, die sich jenseits des 32.767ten Zeichens befinden. Der Nachteil ist: Wir sehen die Markierung nicht, da das Textfeld nach dem Setzen des Fokus immer den Bereich vom ersten Zeichen an liefert.

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

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar