Generic Text Search
(and Replace)

in Visual Basic®



by Rick Meyer       Home

This very brief VB project is an attempt to create a generic search capability for TextBoxes. A replace command is included. The search and replace box is built as a separate Form2 so this will be a two form project. Follow the instructions for each form.
When you run the program you may click the command button to bring up the Form2 dialog and enter your search text, or you may select any text and then press F3 to search for it. Pressing F3 with no selected text will search for the previous search text. If there was no previous search and no text is selected then F3 will bring up the Form2 dialog. After a replace, another search is automatically performed.

See the special instructions at the end of the page for adapting with a RichTextBox.

    Instructions for Form1:

1. Start a new standard exe.
2. On Form1 put a TextBox named Text1.
3. Set the Text1.MultiLine property to True.
4. Set the Text1.Scrollbars property to Vertical.
5. Set the Text1.HideSelection property to False.
6. On Form1 put a CommandButton named Command1.

There is no need to position or size the above controls
on the form since that is all done in the Form_Load.

Now you are ready for the code. Select all of the following code (by clicking on the word 'Option' three times) and copy it to the clipboard [Ctrl][Insert]. Then paste it into the code window of Form1 with [Shift][Insert]. Variable Key
% As Integer
& As Long
! As Single
# As Double
$ As String
Option Explicit

'Suggestion: make the CommandButton a Menu Choice
Private Sub Command1_Click()
    Form2.Show
End Sub

Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)
    Select Case KeyCode
        Case vbKeyF3
            KeyCode = 0
            If Len(Text1.Text) = 0 Then Exit Sub
            Form2.NoShow
    End Select
End Sub

Private Sub Form_Load()
    Command1.Caption = "Search"
    Set Form2.Owner = Me
    Set Form2.OwnerTxtBox = Text1
End Sub

Private Sub Form_Resize()
    Const CmdHt! = 315
    If WindowState = vbMinimized Then Exit Sub
    Command1.Move 0, 0, ScaleWidth, CmdHt
    Text1.Move 0, CmdHt, ScaleWidth, ScaleHeight - CmdHt
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Unload Form2
End Sub
    Instructions for Form2:

1. VB Menu->Project->Add Form (then click Open).
2. On Form2 put a CommandButton named Command1.
3. On Form2 put a CommandButton named Command2.
4. On Form2 put a TextBox named Text1.
5. On Form2 put a TextBox named Text2.
6. On Form2 put a CheckBox named Check1.
7. On Form2 put a CheckBox named Check2.

There is no need to position or size the above controls
on the form since that is all done in the Form_Load.

Now you are ready for the code. Select all of the following code (by clicking on the word 'Option' three times) and copy it to the clipboard [Ctrl][Insert]. Then paste it into the code window of Form2 with [Shift][Insert]. Variable Key
% As Integer
& As Long
! As Single
# As Double
$ As String
Option Explicit

'=======================================================
'    The Declaration to keep the search box on top
'=======================================================
Private Declare Function SetWindowPos _
    Lib "user32" ( _
        ByVal hwnd As Long, _
        ByVal hWndInsertAfter As Long, _
        ByVal x As Long, _
        ByVal y As Long, _
        ByVal CX As Long, _
        ByVal CY As Long, _
        ByVal wFlags As Long) As Long

'=======================================================
'               Private Declarations
'=======================================================
Dim ck1%, ck2%
Dim FindText$, FindStart&, ReplaceText$, SearchText$

'=======================================================
'        Public Declarations and One Procedure
'=======================================================
Public Owner As Form
Public OwnerTxtBox As TextBox

Public Sub NoShow()
    If OwnerTxtBox.SelLength = 0 _
            And Len(FindText) = 0 Then
        Show
    Else
        Act1
        Command1_Click
    End If
End Sub

'=======================================================
'           General Finding Procedures
'=======================================================
Private Function CheckFound&(ByVal start&)
    Static L&, Ls&
    
    L = Len(FindText)
    Ls = Len(SearchText)
    If start > Ls Then start = 1
    
    Do While start > 0 And start <= Ls
        start = InStr(start, SearchText, FindText)
    
        If ck1 <> vbChecked Then Exit Do
        If CheckWord(start - 1, start + L) Then Exit Do
        If start <> 0 Then start = start + 1
    Loop
    
    CheckFound = start
End Function

'Words not deliminated by Chars A-Z, a-z, 0-9, "_"
' Exiting the function early returns default False
Private Function CheckWord(ByVal start&, _
                            ByVal last&) As Boolean
    'Check the leading char
    If start > 0 Then
      Select Case Asc(Mid$(SearchText, start, 1)) Or 32
        Case 97 To 122, 48 To 57, 127
            Exit Function
      End Select
    End If
    
    'Check the trailing char
    If (last) <= Len(SearchText) Then
      Select Case Asc(Mid$(SearchText, last, 1)) Or 32
        Case 97 To 122, 48 To 57, 127
            Exit Function
      End Select
    End If
    
    CheckWord = True
End Function

'=======================================================
'           Mouse and Keyboard Events
'=======================================================
Private Sub Command1_Click()
    Static found&
    
    found = CheckFound(FindStart + 1)
    If found = 0 Then found = CheckFound(1)
    
    If found Then
        FindStart = found
        OwnerTxtBox.SelStart = found - 1
        OwnerTxtBox.SelLength = Len(FindText)
    Else
        MsgBox "Nada"
    End If
End Sub

Private Sub Command2_Click()
    OwnerTxtBox.SelText = Text2
    Check2_Click
    Command1_Click
End Sub

Private Sub Check1_Click()
    ck1 = Check1.Value
End Sub

Private Sub Check2_Click()
    ck2 = Check2.Value
    
    If ck2 = vbChecked Then
        SearchText = OwnerTxtBox.Text
    Else
        SearchText = LCase$(OwnerTxtBox.Text)
    End If
    
    Text1_Change
End Sub

Private Sub Text1_Change()
    If ck2 = vbChecked Then
        FindText = Text1.Text
    Else
        FindText = LCase$(Text1.Text)
    End If
End Sub

Private Sub Text1_GotFocus()
    Text1.SelStart = 0
    Text1.SelLength = Len(Text1.Text)
End Sub

Private Sub Text2_Change()
    ReplaceText = Text2.Text
End Sub

Private Sub Text2_GotFocus()
    Text2.SelStart = 0
    Text2.SelLength = Len(Text2.Text)
End Sub

Private Sub Form_KeyPress(KeyAscii As Integer)
    If KeyAscii = vbKeyEscape Then Unload Me
End Sub

'=======================================================
'           Initialization Procedures
'=======================================================
Private Sub Form_Load()
    Text1.Move 120, 120, 2775, 375
    Text2.Move 120, 600, 2775, 375
    Command1.Move 3000, 120, 855, 375
    Command1.Caption = "Find"
    Command1.Default = True
    Command2.Move 3000, 600, 855, 375
    Command2.Caption = "Replace"
    Check1.Move 120, 1080, 1695, 255
    Check1.Caption = "Whole word only"
    Check2.Move 1920, 1080, 1575, 255
    Check2.Caption = "Match case"
    Caption = "Find & Replace"
End Sub

Private Sub Form_Activate()
    Const MyWidth! = 4095, MyHeight! = 1845
    Static x&, y&, sx&, sy&, tx!, ty!
    
    tx = Screen.TwipsPerPixelX
    ty = Screen.TwipsPerPixelY
    
    x = CLng((Owner.Left + Owner.Width - MyWidth) / tx)
    y = CLng((Owner.Top + MyHeight) / ty)
    sx = CLng(MyWidth / tx)
    sy = CLng(MyHeight / ty)
    
    'Const SWP_NOSIZE = 1
    'Const SWP_NOMOVE = 2
    'Const HWND_TOPMOST = -1
    'Const HWND_NOTOPMOST = -2
    'The big API call for on top
    ' also used here for size and position
    SetWindowPos hwnd, -1, x, y, sx, sy, 0
    
    Act1
    Text1.SetFocus
End Sub

Private Sub Act1()
    If OwnerTxtBox.SelLength Then
        FindText = OwnerTxtBox.SelText
    End If
    
    FindStart = OwnerTxtBox.SelStart + 1

    Text1.Text = FindText
    Text2.Text = ReplaceText
    Check1.Value = ck1
    Check2.Value = ck2
    
    Check2_Click
End Sub
    Instructions for a RichTextBox (simplest):

1. In Form1: Change the RichTextBox.Name Property to Text1.
2. In Form1: Set the RichTextBox.HideSelection Property to False.
3. Code change in Form2: Public OwnerTxtBox As RichTextBox