Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF richTextBox question

If a line of text is wrapped to an additional line, how do I determine programmatically the point in the string where it was broken.

Example: Input string = "This is a test of a wrapped line of text".

      Based on the width of the richTextBox it could display:


            This is a test of a wrapped line of
            text.

What I need to determine is offset in the line of the word(s) that got wrapped. In the above case the word "text".

When I extract the Xaml from the richTextBox, I get the original text unwrapped.

Thanks,

Bob Kerlinger

like image 837
Bob Kerlinger Avatar asked Nov 20 '08 20:11

Bob Kerlinger


1 Answers

The trick I found uses the TextPointer class and its GetCharacterRec method.

RichTextBox holds a FlowDocument. Text in flow documents is contained in a Run object (bit of a simplification, but it works). The code finds the TextPointer at the start of the first Run. It then gets the bounding rectangle of that first character. Next the code walks forward one character at a time, gets a new bounding rectangle and checks if the bottom of the new rectangle is different from the original rectangle. If the bottom is different then we are on a new line. TextPointer can then get the text either before or after the break.

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void inspect(object sender, RoutedEventArgs e)
    {
        TextPointer pointer = FindRun(inBox.Document);

        string textAfterBreak = FindBreak(pointer);

        outBox.Text = textAfterBreak;
    }

    private string FindBreak(TextPointer pointer)
    {
        Rect rectAtStart = pointer.GetCharacterRect(LogicalDirection.Forward);

        pointer = pointer.GetNextInsertionPosition(LogicalDirection.Forward);
        Rect currentRect = pointer.GetCharacterRect(LogicalDirection.Forward);

        while (currentRect.Bottom == rectAtStart.Bottom)
        {
            pointer = pointer.GetNextInsertionPosition(LogicalDirection.Forward);
            currentRect = pointer.GetCharacterRect(LogicalDirection.Forward);
        }

        string textBeforeBreak = pointer.GetTextInRun(LogicalDirection.Backward);
        string textAfterBreak = pointer.GetTextInRun(LogicalDirection.Forward);

        return textAfterBreak;
    }

    private TextPointer FindRun(FlowDocument document)
    {
        TextPointer position = document.ContentStart;

        while (position != null)
        {
            if (position.Parent is Run)
                break;

            position = position.GetNextContextPosition(LogicalDirection.Forward);
        }

        return position;
    }
}

<Window x:Class="LineBreaker.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <RichTextBox Grid.Row="0" Name="inBox">
        </RichTextBox>
        <Button Grid.Row="1" Click="inspect">Find Break</Button>
        <TextBox Name="outBox" Grid.Row="2"/>
    </Grid>
</Window>
like image 111
Mike Two Avatar answered Oct 14 '22 03:10

Mike Two