English 中文(简体)
making a simple search function, making the cursor jump to (or highlight) the word that is searched for
原标题:

I have now used way too long time, trying to figure out a problem, which I didn t think would be that hard.

Here is the deal:

I am writing a small application using C# and WPF.

I have a RichTextBox containing a FlowDocument.

I have added a small textbox and a button below my richtextbox.

The user then types in the word he/she wishes to search for, and presses the button.

The richtextbox will then jump to the first occurrance of that word.

it is enough that it just jumps to the correct line - it can also select, highlight or place the cursor by the word - anything will do, as long as the richTextBox is scrolled to the word.

Continuing to press the button, will then jump to the next occurance of the word, and so on so forth, till the end of the document.

As I said - I thought it to be a simple task - however I am having serious problems figuring this out.

最佳回答

This should do the job:

public bool DoSearch(RichTextBox richTextBox, string searchText, bool searchNext)
{
  TextRange searchRange;

  // Get the range to search
  if(searchNext)
    searchRange = new TextRange(
        richTextBox.Selection.Start.GetPositionAtOffset(1),
        richTextBox.Document.ContentEnd);
  else
    searchRange = new TextRange(
        richTextBox.Document.ContentStart,
        richTextBox.Document.ContentEnd);

  // Do the search
  TextRange foundRange = FindTextInRange(searchRange, searchText);
  if(foundRange==null)
    return false;

  // Select the found range
  richTextBox.Selection.Select(foundRange.Start, foundRange.End);
  return true; 
}

public TextRange FindTextInRange(TextRange searchRange, string searchText)
{
  // Search the text with IndexOf
  int offset = searchRange.Text.IndexOf(searchText);
  if(offset<0)
    return null;  // Not found

  // Try to select the text as a contiguous range
  for(TextPointer start = searchRange.Start.GetPositionAtOffset(offset); start != searchRange.End; start = start.GetPositionAtOffset(1))
  {
    TextRange result = new TextRange(start, start.GetPositionAtOffset(searchText.Length);
    if(result.Text == searchText)
      return result;
  }
  return null;
}

The reason for the for() loop in FindTextInRangeUnfortunately the range.Text strips out non-text characters, so in some cases the offset computed by IndexOf will be slightly too low.

问题回答

I have used a Different Approach. Using a TextBox to Set the Keyword; this Searches for the KeyWord on Button Click. Finds the Keyword; Highlighs it and Focuses on that KeyWord.

    // Index of Current Result Found (Counts Characters not Lines or Results)
    private int IndexOfSearchResultFound;
    // Start Position Index of RichTextBox (Initiated as 0 : Beggining of Text / 1st Char)
    private int StartOfSelectedKeyword;
    private int EndOfSelectedKeyword;

    private void btnSearch_Click(object sender, EventArgs e)
    {
        // Reset Keyword Selection Index. (0 is the Staring Point of the Keyword Selection)
        IndexOfSearchResultFound = 0;

        // Specify the End of the Selected Keyword; using txt_Search.Text.Lenght (Char Ammount).
        EndOfSelectedKeyword = txt_Search.Text.Length;

        // If txt_Search.Text is not Empty
        if (txt_Search.Text.Length > 0)
        {
            // Find Keyword in RichTextBox.Text
            IndexOfSearchResultFound = FindKeyword(txt_Search.Text.Trim(), StartOfSelectedKeyword, rtb_Hosts.Text.Length);

            // If string was found in RichTextBox; Highlight it and Focus on Keyword Found Location
            if (IndexOfSearchResultFound >= 0)
            {
                // Focus on Currently Found Result
                rtb_Hosts.Focus();

                // Highlight the search string
                rtb_Hosts.Select(IndexOfSearchResultFound, EndOfSelectedKeyword);

                // Sets a new Starting Position (after the Position of the Last Result Found)
                // To be Ready to Focus on the Next Result
                StartOfSelectedKeyword = IndexOfSearchResultFound + EndOfSelectedKeyword;
            }
        }
    }


    private int FindKeyword(string _SearchKeyword, int _KeywordSelectionStart, int _KeywordSelectionEnd)
    {
        // De-Select Previous Searched String (Keyword)
        if (_KeywordSelectionStart > 0 && _KeywordSelectionEnd > 0 && IndexOfSearchResultFound >= 0)
        { rtb_Hosts.Undo(); }

        // Set the return value to -1 by default.
        int retVal = -1;

        // A valid Starting index should be specified.
        // if indexOfSearchText = -1, Means that Search has reached the end of Document
        if (_KeywordSelectionStart >= 0 && IndexOfSearchResultFound >= 0)
        {
            // Find Keyword
            IndexOfSearchResultFound = rtb_Hosts.Find(_SearchKeyword, _KeywordSelectionStart, _KeywordSelectionEnd, RichTextBoxFinds.None);

            // Determine whether the text was found in richTextBox
            retVal = IndexOfSearchResultFound;
        }
        // Return the index to the specified Keyword text.
        return retVal;
    }

The only thing I couldn t achieve yet is to return to the 1st Search Result

This is a variant that will find the match closest to the Caret position.

  private TextRange FindText(string findText)
    {
      var fullText = DoGetAllText();
      if (string.IsNullOrEmpty(findText) || string.IsNullOrEmpty(fullText) || findText.Length > fullText.Length)
        return null;

      var textbox = GetTextbox();
      var leftPos = textbox.CaretPosition;
      var rightPos = textbox.CaretPosition;

      while (true)
      {
        var previous = leftPos.GetNextInsertionPosition(LogicalDirection.Backward);
        var next = rightPos.GetNextInsertionPosition(LogicalDirection.Forward);
        if (previous == null && next == null)
          return null; //can no longer move outward in either direction and text wasn t found

        if (previous != null)
          leftPos = previous;
        if (next != null)
          rightPos = next;

        var range = new TextRange(leftPos, rightPos);
        var offset = range.Text.IndexOf(findText, StringComparison.InvariantCultureIgnoreCase);
        if (offset < 0)
          continue; //text not found, continue to move outward

        //rtf has broken text indexes that often come up too low due to not considering hidden chars.  Increment up until we find the real position
        var findTextLower = findText.ToLower();
        var endOfDoc = textbox.Document.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward);
        for (var start = range.Start.GetPositionAtOffset(offset); start != endOfDoc; start = start.GetPositionAtOffset(1))
        {
          var result = new TextRange(start, start.GetPositionAtOffset(findText.Length));
          if (result.Text?.ToLower() == findTextLower)
          {
            return result;
          }
        }
      }
    }

If you want to highlight the match then it d be as simple as changing this method to void and doing this when you found the match:

textbox.Selection.Select(result.Start, result.End);




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签