|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. |