Home > front end >  Searching in WPF Richtextbox based on string index positions (WPF, NET.6 C#)
Searching in WPF Richtextbox based on string index positions (WPF, NET.6 C#)

Time:06-17

I am replacing a TextBox with a RichTextBox in WPF (.NET 6) and had a hard time porting the search code. I've tried TextRange and TextPointer using the GetOffset() methods, but nothing has worked. However, the code below works. It looks like a workaround to me and maybe one of you finds it helpful or knows a better way of doing it.

var textRange = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
var text = textRange.Text;
int startIndex = 0;

// Set the start index to the offset 
if (StartPosition != null) 
{
    startIndex = textRange.Start.GetOffsetToPosition(StartPosition);
}

SearchResultValue searchResult;
var result = text.IndexOf(SearchPattern, startIndex, comparison);

// Any result value of 0 or greater indicates a hit
if (result >= 0) 
{
    // Hit
    var selectionStart = textRange.Start.GetPositionAtOffset(result);

    // Iterate over the index positions until the end of the search pattern. Is there a better way of doing it?
    var currentSelection = textRange.Start.GetPositionAtOffset(result);
    
    for (int run = 0; run < SearchPattern.Length; run  ) 
    {
        currentSelection = currentSelection.GetNextInsertionPosition(LogicalDirection.Forward);
    }

    var selectedRange = new TextRange(selectionStart, currentSelection);

    searchResult = new SearchResultValue(true, selectedRange);
} 
else 
{
    // No hit
    searchResult = new SearchResultValue(false, null);
}

return searchResult;

/// <summary>
/// Specifies the result of the search operation
/// </summary>
/// <param name="Successful">true if any content has been found</param>
/// <param name="start">the start index</param>
/// <param name="length">the length</param>
internal record struct SearchResultValue(bool Successful, TextRange ? Selection);

Is there a better way of doing it? I've invested time in browsing the MSDN and other resources and this is the best I could find. But I am new to this control as well.

Thansk!

CodePudding user response:

I could finally solve it. The only solution seems to be parsing each paragraph and search on the text in each of them. To give you an idea of what I mean:

internalSearchPosition is a private TextPointer instance and stores the latest hit. SearchResultValue is just a return value for my view model logic. The entire code is too long for posting, but this is the core of the search functionality.

        while (currentBlock != null)
        {

            var paragraph = currentBlock as Paragraph;
            if (paragraph != null)
            {
                //
                //  Get the range of the paragraph content and search for the pattern
                //
                var paragraphRange = new TextRange(internalSearchPosition, paragraph.ContentEnd);
                var paragraphText = paragraphRange.Text;
                int result = paragraphText.IndexOf(searchPattern, comparison);
                if (result >= 0)
                {
                    //
                    // Hit. Build the search result and leave the for each loop
                    //
                    var startSelection = paragraph.ContentStart.GetPositionAtOffset(result);
                    var endSelection = startSelection.GetPositionAtOffset(searchPattern.Length 1);
                    searchResult = new SearchResultValue(true, new TextRange(startSelection,endSelection));

                    // Save the current position
                    internalSearchPosition = endSelection;

                    break;
                }
            }

            // Go to the next block or leave the loop when NextBlock is null (end of paragraphs). Also reset the internal search position if required.
            currentBlock = currentBlock.NextBlock;
            if (currentBlock != null)
            {
                internalSearchPosition = currentBlock.ContentStart;
            }
        }
  • Related