2

Because TextBox doesn't have a find function, I've created and modified my own version of it to my needs. I've created to functions. One for Search Next and another for Search Previous

My problem is that:

  1. If my search term is more than 1 character long and I've searched for the term four times, IF on the 4th term I decide the click on search previous it goes back to the previous search term and that works fine. Now because I clicked on search previous 4 times and I went to the 3rd search term using previous, IF I decide to go the 4th term again using Find Next, I have to double click on find next and then it selects the 4th term.

  2. If my search term is 1 character long and I want to search for a character, I type the character e.g. 'o' and it goes through each character in the textbox but once I decide to go back using search previous, I have to double click search previous button and then it goes back and if I decide to search next I have to double click again then it searches next.

This might help understand the double click and single click:

http://media.giphy.com/media/3xz2BJgF2DrtcCnP1e/giphy.gif

I've been trying to get this to work for quite a while now and I've had no luck. I have no idea where I'm going with this and before I confuse myself it will be great if someone can help me with this.

My code for:

Variables

public int startPostion = 0;
boolean passedNext;
public int pos = 0;

Search Next

 public bool FindAndSelectNext(string TextToFind, bool MatchCase)
    {
        try
        {
            var mode = MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
            int position = TheTextBox.Text.IndexOf(TextToFind, startPostion, mode);
            pos = position;

            if (position == -1)
            {
                var TheString = TheTextBox.Text;
                var foundposition2 = TheString.IndexOf(TextToFind, mode);

                TheTextBox.SelectionStart = foundposition2;
                TheTextBox.SelectionLength = TextToFind.Length;
                startPostion = foundposition2 + TextToFind.Length;
                TheTextBox.Focus();

                passedNext = false;
                Debug.WriteLine("1");
                return true;

            }
            else
            {
                TheTextBox.SelectionStart = position;
                TheTextBox.SelectionLength = TextToFind.Length;
                startPostion = position + TextToFind.Length;
                TheTextBox.Focus();

                passedNext = true;
                Debug.WriteLine("2");
                return true;
            }

        }
        catch (Exception ex)
        {
            MessageBox.Show(string.Format("Could not find '{0}' in the document.", TextToFind), ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        return true;
    }

Search Previous

public bool FindAndSelectPrevious(string TextToFind, bool MatchCase)
    {
        StringComparison mode = MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
        if (passedNext == true)
        {
            int foundPosition = startPostion < 0 ? TheTextBox.Text.Length : startPostion - 1;
            foundPosition = TheTextBox.Text.LastIndexOf(TextToFind, pos, mode);
            passedNext = false;
            if (foundPosition < 0)
            {
                if (startPostion < 0)
                {
                    MessageBox.Show(string.Format("Could not find '{0}' in the document.", TextToFind), ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return false;
                }
                foundPosition = TheTextBox.Text.LastIndexOf(TextToFind, mode);
                Debug.WriteLine("1p");
            }

            TheTextBox.SelectionStart = foundPosition;
            TheTextBox.SelectionLength = TextToFind.Length;
            startPostion = foundPosition;
            TheTextBox.Focus();
            Debug.WriteLine("2p");
            passedNext = false;
        }
        else
        {
            int foundPosition = startPostion < 0 ? TheTextBox.Text.Length : startPostion;
            foundPosition = TheTextBox.Text.LastIndexOf(TextToFind, foundPosition, mode);

            if (foundPosition < 0)
            {
                if (startPostion < 0)
                {
                    MessageBox.Show(string.Format("Could not find '{0}' in the document.", TextToFind), ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return false;
                }
                foundPosition = TheTextBox.Text.LastIndexOf(TextToFind, mode);
            }

            if (!(foundPosition == -1))
            {
                try
                {
                    int foundPositionz = startPostion < 0 ? TheTextBox.Text.Length : startPostion - 1;
                    foundPositionz = TheTextBox.Text.LastIndexOf(TextToFind, foundPositionz, mode);

                    TheTextBox.SelectionStart = foundPositionz;
                    TheTextBox.SelectionLength = TextToFind.Length;
                    startPostion = foundPositionz;
                    TheTextBox.Focus();
                }
                catch (Exception ex)
                {
                    var TheString = TheTextBox.Text;
                    var foundposition2 = TheString.LastIndexOf(TextToFind, mode);

                    TheTextBox.SelectionStart = foundposition2;
                    TheTextBox.SelectionLength = TextToFind.Length;
                    startPostion = foundposition2;
                    TheTextBox.Focus();
                    Debug.WriteLine("12p");
                }
            }
            else
            {
                MessageBox.Show(string.Format("Could not find '{0}' in the document.", TextToFind), ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

        }
        return true;
    }
Zer0
  • 1,002
  • 1
  • 19
  • 40
  • What you need to do is when you change from one search to the other increment or decrement StartPosition by the length of the search term. Them just make the FindAndSelectPrevious the reverse of the other search function. – Pedro.The.Kid Jan 26 '15 at 10:26
  • @Pedro.The.Kid Thanks for the fast response however I don't understand what you mean by Increment or decrement startPosition? Mind elaborating? – Zer0 Jan 26 '15 at 10:33
  • The first thing before doing a search if() startPostion += TextToFind.Length <-- in one it it + on the other it is - – Pedro.The.Kid Jan 26 '15 at 10:39
  • Why are you using `dynamic`? – Dai Jan 26 '15 at 18:04
  • @Dai it was just converted code from vb.net to C#. I'll fix it. – Zer0 Jan 26 '15 at 18:05

1 Answers1

2

IMHO, you should just find all the matches the first time the user tries to find something, and then keep an index indicating which match is to be selected/highlighted.

For example:

private List<int> _matches;
private string _textToFind;
private bool _matchCase;
private int _matchIndex;

private void MoveToNextMatch(string textToFind, bool matchCase, bool forward)
{
    if (_matches == null ||
        _textToFind != textToFind ||
        _matchCase != matchCase)
    {
        int startIndex = 0, matchIndex;
        StringComparison mode = matchCase ?
            StringComparison.CurrentCulture :
            StringComparison.CurrentCultureIgnoreCase;

        _matches = new List();
        while (startIndex < TheTextBox.Text.Length &&
            (matchIndex = TheTextBox.Text.IndexOf(textToFind, startIndex, mode)) >= 0)
        {
            _matches.Add(matchIndex);
            startIndex = matchIndex + textToFind.Length;
        }

        _textToFind = textToFind;
        _matchCase = matchCase;
        _matchIndex = forward ? 0 : _matches.Count - 1;
    }
    else
    {
        _matchIndex += forward ? 1 : -1;
        if (_matchIndex < 0)
        {
            _matchIndex = _matches.Count - 1;
        }
        else if (_matchIndex >= _matches.Count)
        {
            _matchIndex = 0;
        }
    }

    if (_matches.Count > 0)
    {
        TheTextBox.SelectionStart = _matches[_matchIndex];
        TheTextBox.SelectionLength = textToFind.Length;
        TheTextBox.Focus();
    }
    else
    {
       MessageBox.Show(string.Format(
           "Could not find '{0}' in the document.", TextToFind),
           ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);

    }
}

public bool FindAndSelectNext(string textToFind, bool matchCase)
{
    MoveToNextMatch(textToFind, matchCase, true);
}

public bool FindAndSelectPrevious(string textToFind, bool matchCase)
{
    MoveToNextMatch(textToFind, matchCase, false);
}
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • This is amazing! however I do have one more problem, If I add new text, how do i reinitialize the list to detect the changes. Right now it only searches through the text which has been added without the user. If I add any more text and try to search it wouldn't go through them. Is there any code I need to add to the TextBox's textchanged event to detect the change so it can reinitialize? – Zer0 Jan 28 '15 at 04:52
  • Sure. You can easily modify the above example to suit your needs. For example, you can invalidate the current cached search results simply by setting the `_matches` field to `null`. The next time one of the search methods is called, it would be recreated based on the latest text. The above isn't intended to fully implement whatever specification you have (how could it, since we don't have the full specification? :) )...it's just a way to show the basic technique. – Peter Duniho Jan 28 '15 at 04:56
  • Thanks that worked and it's exactly the way I wanted it to work. Sorry I had taken a long time to test the code otherwise it would have been an instant answer! ;) – Zer0 Jan 28 '15 at 05:11
  • @PeterDuniho I know this is a while back but I'm wondering if you can still help me with something. This works really well however, when I find the text, it jumps to the start of the first occurence of the text and then goes through everything. Is there a way to start the search from the current index of the character and go through? this GIf explins it all, http://recordit.co/W3Hw7ORKFq – Ahmed.C Jul 18 '15 at 12:35
  • _"Is there a way to start the search from the current index of the character and go through"_ -- it's programming. There's always a way. In this case, note that the `_matches` array is simply storing character indexes. So you can on the initial match, after initializing the `_matches` array, scan that array for the first element with a value greater than the current index of the character, and set `_matchIndex` to that element's index. If you have trouble implementing that idea, please post a new question rather than using comments to try to ask the question. – Peter Duniho Jul 18 '15 at 17:38