RichTextBox for WPF | ComponentOne
Working with WPF RichTextBox / Accessing Layout Information
In This Topic
    Accessing Layout Information
    In This Topic

    When C1RichTextBox creates the C1Document layout, it creates a parallel tree composed of C1TextElementView objects. For each C1TextElement in the C1Document tree, there is at least one C1TextElementView that is tasked with its layout and drawing.

    Take this C1Document tree as an example:

    Its corresponding view tree will look like the following:

    Each C1TextElementView provides some basic layout information for its corresponding C1TextElement:

    Multiple C1TextElementViews can be composed to handle layout and drawing for out C1TextElement. When this is done, the C1TextElementView.Content property contains the inner most C1TextElementView in the composition. The content view’s children correspond to the C1TextElementView.Children collection of the associated C1TextElement.

    View composition is used in C1BoxView to handle margin, padding and border for its C1TextElementView.Content view. This means that the origin of each C1BoxView is outside the margin, padding and border box, while the origin of its C1TextElementView.Content is inside.

    C1FlowView takes care of flowing boxes and text into lines. Each line is represented by a C1Line object. Note that C1Lines not only contains single lines of text, but may also contain an entire paragraph. Each C1FlowView contains a list of C1Line, which are always vertically stacked. In turn, each C1Line is composed of C1LineFragments, which are horizontally stacked. C1LineFragments have a reference to the child element whose origin matches the position of the fragment.

    For example, the following code counts the lines in a C1RichTextBox:

    Visual Basic
    Copy Code
    Private Function CountLines(rtb As C1RichTextBox) As Integer
        Dim root = rtb.ViewManager.GetView(rtb.Document)
        Return CountLines(root)
    End Function
    
    Private Function CountLines(view As C1TextElementView) As Integer
        Dim count As Integer = 0
        Dim flow = TryCast(view, C1FlowView)
        If flow IsNot Nothing Then
            For Each line As var In flow.Lines
                If TypeOf line.Fragments.First().Element Is C1Inline Then
                    count += 1
                End If
            Next
        End If
        For Each child As var In view.Children
            count += CountLines(child)
        Next
        Return count
    End Function
    

    C#
    Copy Code
    int CountLines(C1RichTextBox rtb)
    {
        var root = rtb.ViewManager.GetView(rtb.Document);
        return CountLines(root);
    }
    
    int CountLines(C1TextElementView view)
    {
        int count = 0;
        var flow = view as C1FlowView;
        if (flow != null)
        {
            foreach (var line in flow.Lines)
            {
                if (line.Fragments.First().Element is C1Inline)
                {
                    ++count;
                }
            }
        }
        foreach (var child in view.Children)
        {
            count += CountLines(child);
        }
        return count;
    }
    

    At first, the root view is obtained. That's the same as the view associated to root element, so C1RichTextViewManager.GetView is used to get the view of rtb.Document. After that, the view tree is traversed counting the lines in each C1FlowView found. Note that you only count the lines with C1Inline elements; otherwise you would also count paragraphs and other container blocks.