Selecting text in a VBA Userform Text box

Content

Introduction

Userforms are a key way in VBA to interact with the user. Unfortunately, the MSForms library which hosts the controls for the userform is a rather antiquated piece of code with many oddities. Here I demonstrate one of those oddities and show you how to fix it.

The example file

Download the file it contains a userform that demonstrates the problem and the solution.

The issue

The aforementioned file contains one userform, which has a search box and a textbox containing some made-up text. As you start typing in the search box, the first found instance is selected in the text box. A Previous and Next button allow you to navigate from one found item the the next. At least, that is the intention. The screenshot below is in fact an animated gif (hover your mouse over it or tap it to see the animation) but even the still demonstrates the issue: the text selected in the box is not the text we were looking for, the selection is shifted some characters.

Video showing the problematic text selection in a text box

Why does this happen?

To select text in a text box, we use the SelStart and SelLength properties. And you would expect this to work:

Sub SelectText(txtBox As MSForms.Control, txt2Select As String, instance As Long)
    Dim ctl As MSForms.Control
    Dim pos As Long
    Dim ct As Long
    Set ctl = Me.ActiveControl
    If instance > 1 Then
        For ct = 1 To instance
            If pos = 0 Then
                pos = InStr(txtBox, txt2Select)
            Else
                pos = InStr(pos + Len(txt2Select), txtBox, txt2Select)
            End If
        Next
    Else
        pos = InStr(txtBox, txt2Select)
    End If
    If pos > 0 Then
        With Me.tbxText
            .SetFocus
            .SelStart = pos - 1
            .SelLength = Len(txt2Select)
        End With
    Else
        ResetSelection Me.tbxText
    End If
    ctl.SetFocus
End Sub

However, the character counting inside a text box has one idiosyncracy: Each "Enter" counts as two characters!

The solution

The solution is therefore relatively simple: Looking back from the point where we want the selection to start, count the number of vbLf characters and subtract that number from the position:

Private Function CountNewLinesBefore(ByVal textValue As String, ByVal position As Long) As Long
    If position <= 1 Then Exit Function
    CountNewLinesBefore = Len(Left$(textValue, position - 1)) - _
        Len(Replace$(Left$(textValue, position - 1), vbLf, vbNullString))
End Function

Implemented in the routine shown in the previous chapter:

Sub SelectText(txtBox As MSForms.Control, txt2Select As String, instance As Long)
    Dim ctl As MSForms.Control
    Dim pos As Long
    Dim ct As Long
    Set ctl = Me.ActiveControl
    If instance > 1 Then
        For ct = 1 To instance
            If pos = 0 Then
                pos = InStr(txtBox, txt2Select)
            Else
                pos = InStr(pos + Len(txt2Select), txtBox, txt2Select)
            End If
        Next
    Else
        pos = InStr(txtBox, txt2Select)
    End If
    If Me.cbxFix.Value Then
        pos = pos - CountNewLinesBefore(txtBox, pos)
    End If
    If pos > 0 Then
        With Me.tbxText
            .SetFocus
            .SelStart = pos - 1
            .SelLength = Len(txt2Select)
        End With
    Else
        ResetSelection Me.tbxText
    End If
    ctl.SetFocus
End Sub

Note how the code temporarily sets the focus to the text box. If this is omitted, the text box will not scroll to the selected position.

Now it works:

Video showing the fixed text selection in a text box

Frequently asked questions

Why is the highlighted text shifted in a multiline VBA TextBox?
MSForms counts each line break as two characters internally, so a direct SelStart based on InStr can be off in multiline content.

Does this issue happen in single-line text boxes too?
Usually no. The offset problem appears when line breaks are present in multiline text.

Which properties are used to select text in a TextBox?
Use SelStart for the first character and SelLength for the number of characters to select.

Why does pressing Enter affect the selection position?
In this control, Enter introduces a line break that affects internal indexing differently than expected from plain string search positions.

How do I correct the starting position?
Count line-feed characters before the match and subtract that count from the position returned by InStr.

Should I count vbLf, vbCr, or vbCrLf?
For this pattern, counting vbLf in the textbox value is sufficient and is what the article demonstrates.

How do I select the second or third occurrence of a word?
Repeat InStr searches from the end of the previous match until you reach the requested instance.

Why does the code temporarily set focus to the TextBox?
Without focus on the target TextBox, the control may not scroll to show the selected text.

What should happen when no match is found?
Leave the current selection unchanged or clear it, and provide user feedback such as a status label or message.

Can this be made case-insensitive?
Yes. Use an InStr variant with a text-compare mode when searching, then apply the same offset correction.

Does this apply only to Excel?
The behavior is related to MSForms controls, so similar logic can apply in other VBA hosts that use the same control library.

Will this work with long text blocks?
Yes, but repeated searching can become slower. Cache positions if you need very frequent next/previous navigation.

Where can I download the working sample workbook?
Use the download link in The example file section.

Where can I read the short explanation of the bug and fix?
See The issue and The solution for the complete walkthrough.


Comments

Loading comments...