C# – Select Range of Text in WPF RichTextBox (FlowDocument) Programmatically

crichtextboxwpf

I have this WPF RichTextBox and I want to programmatically select a given range of letters/words and highlight it. I've tried this, but it doesn't work, probably because I'm not taking into account some hidden FlowDocument tags or similar. For example, I want to select letters 3-8 but 2-6 gets selected):

var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
var textRange = new TextRange(startPos,endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty,
    new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
    FontWeights.Bold);

I've realised RichTextBox handling is a bit trickier than I thought 🙂

Update: I got a few answers on the MSDN forums: This thread where "dekurver" seid:

The offsets you're specifying are not
character offsets but symbol offsets.
What you need to do is get a
TextPointer that you know is adjacent
to text, then you can add character
offsets.

And "LesterLobo" said:

you will need to loop through the
paragraphs and inlines to find the
Next and then their offsets in a loop
to apply for all appearances of the
specific text. note that when you edit
your text would move but your
highlight wouldnt move as its
associated with the offset not the
text. You could however create a
custom run and provide a highlight for
it…

Would still LOVE to see some sample code for this if someone knows their way around FlowDocuments…

EDIT I got a version of Kratz VB code working, it looks like this:

private static TextPointer GetPoint(TextPointer start, int x)
{
    var ret = start;
    var i = 0;
    while (i < x && ret != null)
    {
        if (ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.Text ||
            ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.None)
            i++;
        if (ret.GetPositionAtOffset(1, 
LogicalDirection.Forward) == null)
            return ret;
        ret = ret.GetPositionAtOffset(1, 
LogicalDirection.Forward);
    }
    return ret;
}

And I use it like this:

Colorize(item.Offset, item.Text.Length, Colors.Blue);

private void Colorize(int offset, int length, Color color)
{
    var textRange = MyRichTextBox.Selection;
    var start = MyRichTextBox.Document.ContentStart;
    var startPos = GetPoint(start, offset);
    var endPos = GetPoint(start, offset + length);

    textRange.Select(startPos, endPos);
    textRange.ApplyPropertyValue(TextElement.ForegroundProperty, 
new SolidColorBrush(color));
    textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
FontWeights.Bold);
}

Best Answer

Public Function GoToPoint(ByVal start As TextPointer, ByVal x As Integer) As TextPointer
    Dim out As TextPointer = start
    Dim i As Integer = 0
    Do While i < x
        If out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.Text Or _
             out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.None Then
            i += 1
        End If
        If out.GetPositionAtOffset(1, LogicalDirection.Forward) Is Nothing Then
            Return out
        Else
            out = out.GetPositionAtOffset(1, LogicalDirection.Forward)
        End If


    Loop
    Return out
End Function

Try this, this should return a text pointer for the given char offset. (Sorry its in VB, but thats what I am working in...)

Related Topic