How do I create a Find Next button for a Find and Replace?

  • (4 Pages)
  • +
  • « First
  • 2
  • 3
  • 4

56 Replies - 3047 Views - Last Post: 16 November 2012 - 12:18 PM Rate Topic: -----

#46 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 06:40 AM

As you read this, bear in mind that I am referring to the way the code stands now, unless sotherwise specified.

View PostJames1992, on 15 November 2012 - 02:08 AM, said:

Am I right in thinking that: Each time the user presses find, it resets function find. Hence we use the find next button because this allows the user to merely identify the next string of text to be selected (if any exists) without actually modifying the stored values. If the user decided to change the stored values, they'd simply select the text again and then press find.
Therefore, because button Find Next does not modify the values, it is in fact button Find where we would write the code for the mode (i.e. whether we want to find whole words or matchcase or both). Correct?

There are a number of ways to do this sort of thing, The Find() function takes between 2 and 4 arguments. The first two are mandatory, and the compiler will report an error without them. The second two are optional. So the call to Find() may include both offset and mode. Examples might be:

     Find(needle, haystack, 25, mode)
     Find(needle, haystack, , mode)


Another way to do this is to have your CheckBox.CheckedChanged subroutine handle the setting of Mode. Of course, you need to provide code for doing that within the Finder class.

Quote

Am I also right in thinking that: As the previous code stood, I wouldn't have seen the difference with or without the "+_offset" as this had a value of 0.

Yes.

Quote

If I were to remove it the "Option Strict On", by default this would make it Off. Would this alter the performance of the program?

No. As lucky3 pointed out, it would make no difference to the operation of the code. However, if you work with those options Off, then turn them On, you might find that you have a lot of errors in data typing or declarations. These errors are often not a problem, except that there can be errors that are important, and that can be difficult to isolate without proper data typing. Consider the following code, working with TextBoxes:

   ' txtSubtotal.Text contains 40.00
   ' txtTax.Text contains 5.00
   txtTotal.text = txtSubtotal.Text + txtTax.Text


With Option Strict Off, txtTotal will end up containing "40.005.00", because a "+" is an overloaded operator for strings, which performs the same operation (concatenation) as an "&".

I like to advise folks to work with both the options On. It makes you think about what you are doing. And isn't as much effort to code with as the possible effort to find an obscure bug.

As I mentioned last night, I am definitely a beginner at OOP, and am hoping that someone will jump in with further comments and recommendations if they see anything that needs improving.
Was This Post Helpful? 0
  • +
  • -

#47 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 07:12 AM

View PostJames1992, on 15 November 2012 - 05:42 AM, said:

I've researched around and can't find any information on 'mode'?

Glad to see you are doing research! The reason you won't find anything relevant is because we are building our own class, and that's the name I happened to assign to the last argument in Find(). I could have called it BobsYourUncle, and it would have run just as well (or as poorly).

Quote

Problem: Let' imagine the richtextbox already has some text in. Now let's say the user selects some text to perform the search on. They now go to type the word they want to find in the textbox. However, they can't. They have to type the word in first and then select the text. Could this be affecting my program? Because even if they type the word and then select the text and then press find, the find and replace continues to search past the point they selected. I'm thinking of creating my forms from scratch. Might be easier to do one thing at a time then just dump some code in.

The answer to that has two parts, really.

1. While the highlighted text in the RichTextBox looks like it has been deselected, it really hasn't. The Selectionstart, SelectionLength, and SelectedText are all still there. You can prove this by Selecting some text in the RichTextBox, then clicking in the TextBox, then hitting the Tab key until the Focus comes around to the RichTextBox again. Voila! The highlight is back.

2. The problem, of course, is that it's poor UI design to lose the highlight when the control loses Focus. It might be better to highlight all the text that matches the search, using .SelectionBackColor, then to select the first instance of the text that fits the search.

A further thought is that we could change the operation somewhat. One thought is to implement something like NotePad++ has (it's free to download and use, and is a GREAT text editor). When you search, a form pops up with all sorts of options. Doing a search will highlight all occurrences of the search string in the entire document, and will move the text in the editor control to show and select the next occurrence past where your cursor is. It's worth a look.

With the way we have implemented things, Replace() is going to be tricky.

I have to go out for a while but I'll be back later. I have implemened the setting of _mode, and have "Wrap" working in bith FindNext() and FindPrev().

I'm attaching the project file, and when I have time later, I'll post the code.

Attached File(s)


Was This Post Helpful? 0
  • +
  • -

#48 James1992  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 130
  • Joined: 30-October 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 08:22 AM

Okay, I do appreciate it once again!
Was This Post Helpful? 0
  • +
  • -

#49 James1992  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 130
  • Joined: 30-October 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 08:53 AM

Let's say I have this code:

If MatchCase.Checked Then
            startPosition = InStr(textToSearch.Text, textToFind.Text)
        Else
            startPosition = InStr(textToSearch.Text, textToFind.Text, CompareMethod.Text)
        End If


The above code checks whether the matchcase check box is either checked or not. If it is checked then it will only return values that have the exact same text.

I have researched around once again and have got the impression that it isn't as straight forward. If I am able to work out the Whole text code then all my problems have been solved.

Suggestions?
Was This Post Helpful? 0
  • +
  • -

#50 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 10:42 AM

View PostJames1992, on 15 November 2012 - 09:53 AM, said:

Let's say I have this code:
...
The above code checks whether the matchcase check box is either checked or not. If it is checked then it will only return values that have the exact same text.

Suggestions?

Yes. To carry on with the new class, in the last code I sent as a .zip file, in the Class file's Find() function, replace the commented-out line with the if/then/else below it.
                '  index = Haystack.IndexOf(Needle, index)
                If (_mode And MODES.MODE_MATCHCASE) > 0 Then
                    index = Haystack.IndexOf(Needle, index, StringComparison.CurrentCulture)
                Else
                    index = Haystack.IndexOf(Needle, index, StringComparison.CurrentCultureIgnoreCase)
                End If


This is the VB.Net way to do it.

This post has been edited by lar3ry: 15 November 2012 - 10:43 AM

Was This Post Helpful? 0
  • +
  • -

#51 James1992  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 130
  • Joined: 30-October 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 12:19 PM

How would you do whole words then? Is that the StringComparison.CurrentCulture?

This post has been edited by James1992: 15 November 2012 - 12:21 PM

Was This Post Helpful? 0
  • +
  • -

#52 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 12:42 PM

View PostJames1992, on 15 November 2012 - 01:19 PM, said:

How would you do whole words then? Is that the StringComparison.CurrentCulture?

No. StringComparison.CurrentCulture is just saying to match case. It basically says to sort or search using the current culture setting, where some languages may have particular requirements for looking for or sorting certain characters.

As for whole words, well, I would probably find the text, then check the characters before and after the found string, and decide whether or not the string constitutes a whole word.

There are a number of handy functions for this, and what you check, and what you decide based on what you check, will determine what you do.

So, I find the word "test", and check the character before and after. If I find that they are noth non-letters (using, perhaps, IsLetter()), or that one of the characters is not a letter and the one on the other end is at the beginning or end of the string, I could call it a whole word. Bear in mind, though, that you might want to look for words that contain numbers, and if so, you need to deal with that, too.

So have a look at IsLetter, IsNumeric, IsPunctuation, IsWhiteSpace, and any others you might find. Some of these may have to be supplemented with separate code to check for specific characters.
Was This Post Helpful? 0
  • +
  • -

#53 James1992  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 130
  • Joined: 30-October 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 02:55 PM

Studying the code used for find and find in sel, rather than having 2 buttons, ive decided to use and if statement to determine what to set index to. Just personalising it now!

And rather than use Find previous, I'm going to use the similar idea for search backwards. As in, set the index to the lowest position found and then work backwards. I finally see what you mean about what you've been teaching me. I'm truly grateful. I take my hat off to you Sir!

Just a quick question, what do you mean by this code?

Debug.Print(CStr(_indexarray(_arraypointer) + _offset))



Also, why do we create another class? How come we choose not to just put all the functions in the same form?
Was This Post Helpful? 1
  • +
  • -

#54 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 03:10 PM

View PostJames1992, on 15 November 2012 - 03:55 PM, said:

Studying the code used for find and find in sel, rather than having 2 buttons, ive decided to use and if statement to determine what to set index to. Just personalising it now!

That's the spirit!

Quote

And rather than use Find previous, I'm going to use the similar idea for search backwards. As in, set the index to the lowest position found and then work backwards. I finally see what you mean about what you've been teaching me. I'm truly grateful. I take my hat off to you Sir!

Just a quick question, what do you mean by this code?

Debug.Print(CStr(_indexarray(_arraypointer) + _offset))


When you run the program from the debugger (Visual Studio), you will see, at the bottom, an Output window. Debug.Print() is a way to output a string to the debug window. It lets you see what's happening. You can remove those if you don't need them any more, or leave them in. When you run the program standalone, the Debug.Print will output to a separate debugger program, but other than that, it will not be seen.

Quote

Also, why do we create another class? How come we choose not to just put all the functions in the same form?

Well, the main thing is that it encapsulates the variables, subroutines and functions. You don't have to have all that stuff mixed in with the code that's calling stuff within the class. As well, you can later use it in another project, VERY easily. Just save it separately, if you want to locate it easily, in a directory calles "Classes" or similar, and the next time you have a need for it, just add it to your project and start using it.

It's a good idea to separate your GUI from the work, if you can. This class will work with any control that carries a string, or with a string variable, etc., so it even works with Console applications. As long as you can pass it a string and a search term, it'll do its job without you fiddling with code to let it know about the stuff that's calling it.
Was This Post Helpful? 0
  • +
  • -

#55 James1992  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 130
  • Joined: 30-October 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 03:22 PM

I have yet another question.

Let's say you have 2 textboxes. And whatever you write in 1 text box, you want to mimic the exact same thing in the other text box. Pretty much like a mirror. You can begin writing in one text box and then begin deleting the text into the other textbox. Hopefully you know what I mean.

The code I've created was:

If textbox1.Text.Length > 0 Then
            textbox2.Text = textbox1.text
        Else
            textbox2.Text = ""
        End If

        If textbox2.Text.Length > 0 Then
            textbox1.Text = textbox2.Text
        Else
            textbox1.Text = ""
        End If



Now this code does allow you to enter text into one textbox, but if you then try starting to delete it in the other text box, it doesn't allow you :S
Was This Post Helpful? 0
  • +
  • -

#56 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 15 November 2012 - 03:47 PM

This is probably best asked in another thread, but basically, you have code that does two things, one after the other. If you try to make changes in TextBox2, and then execute the code to copy TextBox1 into TextBox2... well, you can see the problem.
A lot depends on when each chunk of code is executed.
Was This Post Helpful? 0
  • +
  • -

#57 lar3ry  Icon User is offline

  • Coding Geezer
  • member icon

Reputation: 310
  • View blog
  • Posts: 1,290
  • Joined: 12-September 12

Re: How do I create a Find Next button for a Find and Replace?

Posted 16 November 2012 - 12:18 PM

I've updated the Finder class to handle Whole Word searches. Here's the code for it.
Option Strict On
Option Explicit On

Class Finder
    Public Enum MODES As Byte
        DEFAULTS = 0
        MATCHCASE = 1
        WHOLEWORD = 2
        WRAP = 4
        WRAPWARN = 8
    end enum

    ' Dim some variables to be set by the call to Find()
    Private _needle As String   ' What we are looking for
    Private _haystack As String ' where we are looking for it
    Private _indexarray(20) As Integer ' storage for all indexes
    Private _arraypointer As Integer   ' and index into _indexarray
    Private _maxpointer As Integer ' end of array (points to a -1 entry)
    Private _offset As Integer = 0  ' an offset to add to the indexes
    Private _mode As Byte ' for our optional modes

    Public Sub SetMode(ByVal Mode As MODES)
        Debug.Print(CStr(Mode))
        If Mode = 0 Then
            _mode = 0
        Else
            _mode = _mode Or Mode
        End If
        Debug.Print(CStr(_mode))

    End Sub

    Public Sub ClearMode(ByVal Mode As MODES)
        Debug.Print(CStr(Mode))
        _mode = _mode And Not Mode
        Debug.Print(CStr(_mode))
    End Sub

    Public Function Find(ByVal Needle As String,
                           ByVal Haystack As String,
                           Optional offset As Integer = 0,
                         Optional Mode As MODES = 0) As Integer
        ' Set variables Dim'd above
        If Needle = "" Or Haystack = "" Then
            Find = -1
        Else
            _needle = Needle
            _haystack = Haystack
            _offset = offset
            If Mode <> 0 Then
                _mode = Mode
            End If
            Dim index As Integer ' a local variable
            _indexarray(0) = -1   ' Allows us to seacrh backward and detect to beginning of string
            _arraypointer = 1
            While index <> -1
                'Dim modetemp = _mode And MODES.MATCHCASE
                If (_mode And MODES.MATCHCASE) > 0 Then
                    index = Haystack.IndexOf(_needle, index, StringComparison.CurrentCulture)
                Else
                    index = Haystack.IndexOf(_needle, index, StringComparison.CurrentCultureIgnoreCase)
                End If
                Debug.Print(CStr(index))
                If index <> -1 Then
                    If (_mode And MODES.WHOLEWORD) > 0 Then
                        If IsWholeWord(index) Then
                            _indexarray(_arraypointer) = index
                            _arraypointer += 1
                        End If
                    Else
                        _indexarray(_arraypointer) = index
                        _arraypointer += 1
                    End If
                    index += 1
                End If
            End While
            _indexarray(_arraypointer) = -1  ' to indicate no more instances of _needle
            _maxpointer = _arraypointer
            _arraypointer = 1
            If _indexarray(1) = -1 Then
                Find = -1
            Else
                Find = _indexarray(_arraypointer) + _offset
            End If
        End If
        Debug.Print(CStr(_indexarray(_arraypointer) + _offset))
    End Function

    Public Function FindNext() As Integer
        ' Uses index array and a pointer already created with Find
        ' Uses index array and a pointer already created with Find
        _arraypointer += 1
        FindNext = _indexarray(_arraypointer)
        If FindNext = -1 Then
            If CBool(_mode And MODES.WRAP) Then
                _arraypointer = 1
                FindNext = _indexarray(_arraypointer) + _offset
            Else
                FindNext = -1
            End If
        Else
            FindNext += _offset
        End If
        Debug.Print(CStr(_indexarray(_arraypointer) + _offset))
    End Function

    Public Function FindPrev() As Integer
        ' Uses index array and a pointer already created with Find
        _arraypointer -= 1
        FindPrev = _indexarray(_arraypointer)
        If FindPrev = -1 Then
            If CBool(_mode And MODES.WRAP) Then
                _arraypointer = _maxpointer - 1
                FindPrev = _indexarray(_arraypointer) + _offset
            Else
                FindPrev = -1
            End If
        Else
            FindPrev += _offset
        End If
        Debug.Print(CStr(_indexarray(_arraypointer) + _offset))
    End Function

    Private Function IsWholeWord(ByVal idx As Integer) As Boolean
        Dim firstpart As Boolean = True
        Dim secondpart As Boolean = True
        If (idx = 0) Then
            firstpart = True
        Else
            If (Char.IsLetter(_haystack(idx - 1))) Or (Char.IsLetter(_haystack(idx - 1))) Then
                firstpart = False
            End If
        End If
        If (idx + _needle.Length) = _haystack.Length Then
            secondpart = True
        Else
            If (Char.IsLetter(_haystack(idx + _needle.Length))) Or (Char.IsLetter(_haystack(idx + _needle.Length))) Then
                secondpart = False
            End If
        End If
        Return firstpart And secondpart
    End Function

End Class


Was This Post Helpful? 0
  • +
  • -

  • (4 Pages)
  • +
  • « First
  • 2
  • 3
  • 4