views:

3031

answers:

4

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);
}
A: 

Try that :

var textRange = MyRichTextBox.Selection;
var start = MyRichTextBox.Document.ContentStart;
var startPos = start.GetPositionAtOffset(3);
var endPos = start.GetPositionAtOffset(8);
textRange.Select(startPos, endPos);
textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
Thomas Levesque
Doesn't seem to work properly I'm afraid.
Johan Danforth
I just tried, it works for me...
Thomas Levesque
@Tomas not for me I'm afraid. letters 2-6 gets selected/colored for me using that code. I'm going to try something else and get back here.
Johan Danforth
+2  A: 
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...)

Kratz
Nice! I got a version of that code working, will add it to the question. Cheers.
Johan Danforth
This is also handy for counting the characters in a RichTextBox: just do the loop while `out` is not null and return `i` at the end.
chaiguy
A: 

Incidentally (and this may be academic to all but myself), if you set FocusManager.IsFocusScope="True" on the container of the RichTextBox, a Grid for example,

<Grid FocusManager.IsFocusScope="True">...</Grid>

then you should be able to use Johan Danforth's Colorize method without the two invocations of ApplyPropertyValue, and the RichTextBox ought to use the default selection Background and Foreground to highlight the selection.

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);
}

Have not tried it with the RichTextBox, but it works quite well when templating a find TextBox in a FlowDocumentReader. Just to be sure you might also set

<RichTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">...</RichTextBox>

to ensure the RichTextBox has focus within its focus scope.

The downside of this, of course, is that if the user clicks or performs a selection within the RichTextBox, your selection disappears.

Benjamin
A: 

Is there anyway of doing this without using textRange.Select as it moved selection and scroll to the last highlighted string in the RTB.

Steve