|
|
![]() |
|
||
|
|
|||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Visual Basic Projects Pure VB: Create a Find and Replace Dialog Step 1: Introduction, Layout and Calling Form Code |
|
| Posted: | Monday August 05, 2002 |
| Updated: | Monday December 26, 2011 |
| Applies to: | VB4-32, VB5, VB6 |
| Developed with: | Original: VB3/Win 3.1. Updated: VB6/Windows XP |
| OS restrictions: | None |
| Author: | VBnet - Randy Birch |
| Other project pages: | Step 2: Building the Find/Replace Form |
| Prerequisites |
| None. |
|
|
Time
to dust off some really old code to provide some new functionality to VB!I wrote this initial search/replace routine from scratch way back in VB2/3 days, as I needed this functionality in a utility I wrote for myself to format scientific text into decent slide format. With the release of Win9x I have routinely hauled out Window's API-based Find & Replace dialog code in an attempt to get that puppy working under VB. Provided as Windows' standard common dialog (like notepad's Find dialog), each attempt was met with limited, flaky and unpredictable success. So tonight I dusted off my old VB3 code, removed all the formerly-familiar data type declaration characters and, with nary an API in sight (for a change), present the code here as viable way to add Find and/or Replace functionality to a VB app. Because we're all comfortable with VB's own Find & Replace dialogs, I decided to base my updated version on those familiar dialogs. This demo mimics the layout of those VB dialogs and with the features built in to the code using them should be just as intuitive as using VB's own. The features includes:
When the Find or Replace dialog has been invoked without the user making a text selected in the target control, the dialog recognizes this and runs a routine that examines characters to either side of the current cursor position in that control, looking for a word-break delimiter (delimiters you can add to edit as required). When recognized delimiters are located on either side of the cursor position, the routine returns the word between those delimiters as the potential Find search word. For example, if the cursor was between the u and b in Public, the routine would present Public as the proposed search word. When the dialog has been invoked and the user has made a text selection, that selection is presented as the search word regardless of whether its a legitimate word (ie if ub was selected in Public, ub would be the search word proposed. Finally, if the cursor is on a blank line, no word is proposed. Similarly, because a public UDT defined public in a bas module is used to hold many of the counting and tracking parameters, the dialog will automatically display the last-entered Replace string on subsequent Replace actions. There is a lot going on in this code, therefore it's obscenely commented with most routines remarkably compact once my embedded novels are removed. There is a wee bit of hard-coding in the SetupInit sub in dlgReplace that may futz up display on large-font systems - this was only done for expediency and can easily be soft-coded to use the proper screen resolution info to position the controls. SetupInit is the procedure responsible for showing, hiding and repositioning controls based on the dialog type shown. The Find Whole Word Only, search Direction, and the Search range (current window, all windows etc.) are not coded in this demo and are left for you to implement if desired. In addition, the control does not save a Most-Recently-Used list of entries, though this too is simple enough to implement. The layout form below shows the names of the respective controls you must add and their relative positions on the form. The controls in gold indicate controls that are required and referenced in code in this demo but that do not have any code attached (per the statement above). The second cropped form below is provided as a convenience and can be assigned to your working form's Picture property to provide a rough layout guide while you develop/align the controls. Because of the hard-coded values mentioned above, it is strongly recommended you use the template for layout while testing and examining this app. Once you've soft-coded the SetupInit code, the initial position of the controls is less of an issue. This project is divided into two pages ... this page with the form design, BAS module and main form code (not illustrated), and a second page with the complete routine for the Find/Replace dialog. The main form requires only a multiline textbox and two default command buttons.
|
| BAS Code: FindReplace.bas |
|
|
| Place the following code into the general declarations area of a bas module and save the file as FindReplace.bas: |
|
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'constants used to specify the
'action taken on dialog startup
Public Const swFindText As Boolean = 0
Public Const swReplaceText As Boolean = 1
Public Type FindReplaceData2
'Strings
'the text to search
sWorkText As String
'the string to search for
sSearchText As String
'string to replace it with
sReplaceText As String
'Flags
'if true Trim leading & trailing spaces
bTrimSpaces As Boolean
'flag indicating whether to match case.
'Treated as a Boolean, but declared as
'int as the values must be 0 or 1 (not -1)
bMatchCase As Integer
'flag to force the search to start
'at the top of the current document
bStartAtTop As Integer
'flag to chose between find/replace
'or count-only routines
bCountOnly As Boolean
'flag indicating counting will take
'place in the replace sub
bInReplaceMode As Boolean
'Position trackers
'cursor position when the dialog called
nCursorPos As Long
'Tracks the position of the found word.
'When dialog starts, holds cursor position
'within the work text
nCurrPos As Long
'Counters
'number of matches and replacements made
nNumFound As Long
nNumReplaced As Long
End Type
Public frd As FindReplaceData2
Public Function CreateInitialSearchString(ctl As Control) As String
Dim initSelLen As Long
Dim work As String
Dim wordStart As Long
Dim wordEnd As Long
Dim currPos As Long
Dim part As String
Dim initSelStart As Long
Dim BreakChrs As String
'was something selected in the
'text box?
initSelLen = Len(ctl.SelText)
'yes, so use that as the search criteria
'and save start of the current selection
'to a variable
If initSelLen > 0 Then
CreateInitialSearchString = ctl.SelText
currPos = ctl.SelStart - initSelLen
Exit Function
End If
'nothing was selected, so try and find
'the next adjacent word to the cursor
initSelStart = ctl.SelStart
'typical delimiters used to separate words
BreakChrs = ",()<>[]\|:;=/*-+" & _
Chr$(32) & _
Chr$(13) & _
Chr$(10) & _
Chr$(9) & _
Chr$(39)
'create a buffer of the
'passed control contents
work = ctl.Text
'if the cursor is presently at position 0,
'set the cursor position variable to 1.
'If further in the text, use that value.
currPos = IIf(initSelStart > 0, initSelStart, 1)
'from the current cursor position in currPos,
'work backwards to find a break char and
'save that value as wordStart
For wordStart = currPos To 1 Step -1
If InStr(BreakChrs, Mid$(work, wordStart, 1)) Then
Exit For
End If
Next wordStart
'because the code above found a
'break chr, add 1 to obtain the
'first letter of the search word
'and clip the work text from that
'point on
wordStart = wordStart + 1
part = Mid$(work, wordStart, Len(work))
'starting at currPos (which is now 1 since
'the preceding text was nuked in the
'above call), work forward to find the
'next break char.
For wordEnd = 1 To Len(part)
If InStr(BreakChrs, Mid$(part, wordEnd, 1)) Then
Exit For
End If
Next wordEnd
'again, since we know where the break
'chr is, subtract one for the last
'letter of the word
wordEnd = wordEnd - 1
'return the word determined above
If wordEnd + wordStart > wordStart Then
CreateInitialSearchString = Mid$(work, wordStart, wordEnd)
End If
End Function
Public Function CreateInitialReplaceString() As String
'if there is already a value
'saved as the replace text,
'return that
If Len(frd.sReplaceText) > 0 Then
CreateInitialReplaceString = frd.sReplaceText
Exit Function
Else
CreateInitialReplaceString = ""
End If
End Function
Public Sub CentreFormInParent(cform, pform)
'cform = child form to centre
'pform = parent form to centre in
cform.Move (pform.Left + ((pform.Width - cform.Width) \ 2)), _
(pform.Top + ((pform.Height - cform.Height) \ 2))
End Sub
|
| Form Code: Form1 - the main application form |
|
|
|
To
Form1 add two command buttons (Command1, Command2) and a text box (Text1)
with the following code. Don't try to run this form until the dlgReplace
form from page 2 of this project has been created and saved.
In order to see the "found text" highlighted in the main form textbox the Text1 HideSelection property must be set to False at design time. In order to have the find/replace dialogs shown when the Control keys (F or H) are pressed, the form's KeyPreview needs to be set to True as well. |
|
|
Option Explicit Dim killBackspace As Boolean
Private Sub Form_Load()
Dim openfilename As String
Dim fileNum As Integer
'just a local file to search - change as desired
openfilename = "findreplace.bas"
Me.Caption = "VBnet Find/Replace Dialog Demo"
fileNum = FreeFile
Open openfilename For Input As fileNum
Text1.Text = Input$(LOF(fileNum), fileNum)
Close fileNum
Command1.Caption = "Find..."
Command2.Caption = "Replace..."
End Sub
Private Sub Command1_Click()
Dim sSearch As String
'determine the search text - pass the *control*, not the Text property
sSearch = CreateInitialSearchString(Text1)
With frd
.bCountOnly = False
.sSearchText = sSearch
If .bTrimSpaces Then .sSearchText = Trim$((sSearch))
With dlgReplace
'show find dlg
.SetupInit swFindText, Text1, Me
.Show , Me
End With
End With
End Sub
Private Sub Command2_Click()
Dim sSearch As String
Dim sReplace As String
'determine text to pass as
'sSearch and sReplace
sSearch = CreateInitialSearchString(Text1)
sReplace = CreateInitialReplaceString()
With frd
.bCountOnly = False
.sSearchText = sSearch
.sReplaceText = sReplace
If .bTrimSpaces Then .sSearchText = Trim$((sSearch))
With dlgReplace
'show replace dlg
.SetupInit swReplaceText, Text1, Me
.Show , Me
End With
End With
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
'ctrl+H is also the keycode for a backspace
'in a textbox, so in order to display the
'replace form on a Ctrl+H, we need to eat
'the keystroke.
'
'To do this, we set a flag indicating that
'the backspace ctrl combination had been
'pressed and respond show the dialog instead,
'eating the keystroke in the KeyPress event,
'and resetting it in KeyUp. This does not
'interfere with normal backspace key presses.
If (Shift) = 2 Then
Select Case KeyCode
Case 72 'H - replace
killBackspace = True
Command2.Value = True
Case 70 'F - find
killBackspace = True
Command1.Value = True
Case Else
End Select
End If
End Sub
Private Sub Form_KeyPress(KeyAscii As Integer)
'if the ctrl+ backspace combo flag
'set, eat the keystroke
If killBackspace Then KeyAscii = 0
End Sub
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
If killBackspace = True Then killBackspace = False
End Sub |
|
|
| Comments |
| Save the project thus far and move on to Step 2: Building the Find/Replace Form. |
|
|
|
|
|
|||||
|
|||||
|
|
|||||
|
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |
![]() |