Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highlight text in RichTextBox

I'm trying to use a RichTextBox and my first feeling : "What's it's complicated to use !"... Amazing ...

So I'm trying to highlight a text contained in my RichTextBox.

I currently have the following code:

TextRange range = new TextRange(MyTextInput.Document.ContentStart, MyTextInput.Document.ContentEnd);
range.Text = @"TOP a multiline text or file END";
Regex reg = new Regex("(top|file|end)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

foreach (Match match in reg.Matches(range.Text))
{
    TextPointer start = range.Start.GetPositionAtOffset(match.Index, LogicalDirection.Forward);
    TextPointer end = range.Start.GetPositionAtOffset(match.Index + match.Length, LogicalDirection.Backward);
    // text contains the exact match I want
    string text = range.Text.Substring(match.Index, match.Length);
    // here the highlighted text isn't the text I searched...
    TextRange textrange = new TextRange(start, end);
    textrange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
    textrange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
}

TOP is correctly highlighted but not file or end but highlight me or.

Any suggestions?

like image 901
Arnaud F. Avatar asked Apr 16 '11 09:04

Arnaud F.


1 Answers

you have to imagine what the RichTextBox does under the hood to understand the behavior. I don't know exactly but I imagine the following: Line 1-2 set as Content of RichTextBox a Paragraph with a Run.

Then with the first iteration with ApplyPropertyValue the Content of the RichTextBox gets changed! It now contains a Paragraph with a Span (with a Run inside) and a Run.

And then you have to consider the discrepancy between the Regex match and GetPositionAtOffset. The Regex match returns an index for a char position in a string.

GetPositionAtOffset uses "An offset, in symbols, for which to calculate and return the position" where a symbol is:

  • An opening or closing tag for the TextElement element.
  • A UIElement element contained in an InlineUIContainer or BlockUIContainer. Note that such a UIElement is always counted as exactly one symbol; any additional content or elements contained by the UIElement are not counted as symbols.
  • A 16-bit Unicode character inside of a text Run element.

So what you might want to do is something like this:

TextRange range = new TextRange(MyTextInput.Document.ContentStart, MyTextInput.Document.ContentEnd);
range.Text = @"TOP a multiline text or file END";
Regex reg = new Regex("(top|file|end)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

var start = MyTextInput.Document.ContentStart;
while (start != null && start.CompareTo(MyTextInput.Document.ContentEnd) < 0)
{
    if (start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
    {
        var match=reg.Match(start.GetTextInRun(LogicalDirection.Forward));

        var textrange = new TextRange(start.GetPositionAtOffset(match.Index, LogicalDirection.Forward), start.GetPositionAtOffset(match.Index + match.Length, LogicalDirection.Backward));
        textrange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
        textrange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
        start= textrange.End; // I'm not sure if this is correct or skips ahead too far, try it out!!!
    }
    start = start.GetNextContextPosition(LogicalDirection.Forward);
}

*Disclaimer: I have not tried this as right now I'm nowhere near a development environment. I don't even know if this compiles, but I hope so.

like image 165
Markus Hütter Avatar answered Nov 08 '22 07:11

Markus Hütter