none
RichTextBox / Flowdocument ???????? RRS feed

  • Question

  • I have now spent hours (about 20) trying to get some simple things done with the FlowDocument in code.  As you can see by asking a question it has been in vain.  The API for dealing with a FlowDocument is ..... (couldn't think of a non swear word to use).

    A simple thing like selecting a range of text is well impossible.  If you have multiple fonts, weights etc then forget about it.

    Is there anywhere a document that outlines the API and how to use it to do simple things?  

    Are there any pages with "working" samples?  The ones I have seen (and I've seen hundreds) are for the most part a pipe dream and do not work.

    A simple example.  Say I have a text of just one word of 4 chars.  I have it set so that there are two font sizes as follows:

    <FlowDocument>
      <Paragraph>
         <Run FontSize="20">So</Run><Run>me bold text in the paragraph.</Run>
         <Run>Some text that is not bold.</Run>
       </Paragraph>
    
    etc etc.
    

    When the word  (Some) is selected the selection ranges shows the full text.  An attempt to get the fontsize fails as there are two.  I can get the first part of the text but have not been able to get the rest.  At the current rate of development using RTF is a definite no go.



    Lloyd Sheen

    Sunday, August 18, 2019 9:31 PM

Answers

  • Ok I have two methods one for retrieving all TextRanges within a FlowDocument and one for selecting all the TextRangess within a selection of the FlowDocument.  In VB but they should be easy to convert.

    Get all TextRanges

    Public Function GetALLTextRanges(rtb As RichTextBox) As List(Of TextRange)
            Dim changeRanges As New List(Of TextRange)
    
            Dim startPointer As TextPointer
            Dim endPointer As TextPointer
    
            Dim thePointerContext As TextPointerContext
            Dim previousPointerContext As TextPointerContext
    
            Dim whatToChange As TextPointer = rtb.Document.ContentStart
            Dim selectionLength As Integer = 0
    
            ' start looking for text ranges starting with pointer 0
            Dim positionPointer As Integer = 0
    
            ' we will loop thru the document until either a TextPointerContext is None
            ' indicating no more data or if there is an error
    
            While True
                startPointer = whatToChange.GetPositionAtOffset(positionPointer)
                Try
                    thePointerContext = startPointer.GetPointerContext(LogicalDirection.Forward)
    
                    ' if there are no more textpointers in the document
    
                    If thePointerContext = TextPointerContext.None Then
                        Exit While
                    End If
    
                    ' if the TextPointerContext indicates the start of an element and 
                    ' we are within a TextPointerContext of Text set up the TextRange
                    ' with the Start as the current TextPointer and the End as the TextPointer
                    ' for the last char of the text range
    
                    If (thePointerContext = TextPointerContext.Text And previousPointerContext = TextPointerContext.ElementStart) Or
                        (thePointerContext = TextPointerContext.Text And positionPointer = 0) Then
                        Dim text = startPointer.GetTextInRun(LogicalDirection.Forward)
                        Dim textlen As Integer = text.Length
                        endPointer = whatToChange.GetPositionAtOffset(positionPointer + textlen)
                        Dim tr As TextRange = New TextRange(startPointer, endPointer)
                        changeRanges.Add(tr)
    
                        ' shift pointer by length of last text
                        positionPointer += textlen + 1
                    Else
                        ' get the next char pointer
                        positionPointer += 1
                    End If
                Catch ex As Exception
                    Exit While
                End Try
    
                previousPointerContext = thePointerContext
            End While
            Return changeRanges
    
        End Function

    Get selection TextRanges

    Public Function GetSelectedTextRanges(rtb As RichTextBox) As List(Of TextRange)
            Dim changeRanges As New List(Of TextRange)
    
            'If rtb.Selection.IsEmpty Then
            '    Return changeRanges
            '    Exit Function
            'End If
    
            Dim startPointer As TextPointer
            Dim endPointer As TextPointer
    
            Dim thePointerContext As TextPointerContext
            Dim previousPointerContext As TextPointerContext
    
            Dim whatToChange As TextPointer = rtb.Selection.Start
            Dim selectionLength As Integer = 0
    
    
            Dim selectionText As String = rtb.Selection.Text
    
            ' get rid of List extra characters because the FlowDocument API is 
            ' really screwy at this point 
            ' the function GetTextInRun shows the text while the text range has 
            ' extra chars for things like ListItem and TableCell
            ' we remove the extraneous chars to make the lenght of the selected items
            ' to match what is in the Run
    
            selectionText = selectionText.Replace(Chr(149) & vbTab, String.Empty)
    
            ' get rid of TableCell extra characters
            selectionText = selectionText.Replace(vbTab, String.Empty)
            selectionText = selectionText.Replace(vbCrLf, String.Empty)
    
            ' now set the selection length
    
            selectionLength = selectionText.Length
    
            'whatToChange = rtb.Selection.Start
    
            Dim positionPointer As Integer = 0
            While selectionLength > 0
                startPointer = whatToChange.GetPositionAtOffset(positionPointer)
                Try
                    thePointerContext = startPointer.GetPointerContext(LogicalDirection.Forward)
                    If (thePointerContext = TextPointerContext.Text And previousPointerContext = TextPointerContext.ElementStart) Or
                        (thePointerContext = TextPointerContext.Text And positionPointer = 0) Then
                        Dim text = startPointer.GetTextInRun(LogicalDirection.Forward)
                        Dim textlen As Integer = text.Length
    
                        If textlen > selectionLength Then
                            textlen = selectionLength
                            Dim t As Integer = 1
                        End If
                        endPointer = whatToChange.GetPositionAtOffset(positionPointer + textlen)
                        Dim tr As TextRange = New TextRange(startPointer, endPointer)
                        changeRanges.Add(tr)
                        selectionLength -= textlen
    
                        ' shift pointer by length of last text
                        positionPointer += textlen + 1
    
                    Else
                        ' get the next char pointer
                        positionPointer += 1
    
                    End If
                Catch ex As Exception
                    Exit While
                End Try
    
                previousPointerContext = thePointerContext
            End While
            Return changeRanges
    
        End Function


    Lloyd Sheen

    • Marked as answer by sqlguy Monday, August 26, 2019 5:19 PM
    Monday, August 26, 2019 3:50 PM

All replies


  • Hi sqlguy,

    > I can get the first part of the text but have not been able to get the rest. 

    There should be no direct way to get different values for a single property of the RichTextBox selection fonts which include multiple Run now. TextPointer may give you some help to get the position of the FolwDocument, the method GetTextInRun/GetTextRunLength may help you to capture the selected text depending on which Run it is in , then get the FontSizeProperty for each Run.


    Best regards

    Yong Lu

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, August 23, 2019 10:12 AM
    Moderator
  • Ok I have two methods one for retrieving all TextRanges within a FlowDocument and one for selecting all the TextRangess within a selection of the FlowDocument.  In VB but they should be easy to convert.

    Get all TextRanges

    Public Function GetALLTextRanges(rtb As RichTextBox) As List(Of TextRange)
            Dim changeRanges As New List(Of TextRange)
    
            Dim startPointer As TextPointer
            Dim endPointer As TextPointer
    
            Dim thePointerContext As TextPointerContext
            Dim previousPointerContext As TextPointerContext
    
            Dim whatToChange As TextPointer = rtb.Document.ContentStart
            Dim selectionLength As Integer = 0
    
            ' start looking for text ranges starting with pointer 0
            Dim positionPointer As Integer = 0
    
            ' we will loop thru the document until either a TextPointerContext is None
            ' indicating no more data or if there is an error
    
            While True
                startPointer = whatToChange.GetPositionAtOffset(positionPointer)
                Try
                    thePointerContext = startPointer.GetPointerContext(LogicalDirection.Forward)
    
                    ' if there are no more textpointers in the document
    
                    If thePointerContext = TextPointerContext.None Then
                        Exit While
                    End If
    
                    ' if the TextPointerContext indicates the start of an element and 
                    ' we are within a TextPointerContext of Text set up the TextRange
                    ' with the Start as the current TextPointer and the End as the TextPointer
                    ' for the last char of the text range
    
                    If (thePointerContext = TextPointerContext.Text And previousPointerContext = TextPointerContext.ElementStart) Or
                        (thePointerContext = TextPointerContext.Text And positionPointer = 0) Then
                        Dim text = startPointer.GetTextInRun(LogicalDirection.Forward)
                        Dim textlen As Integer = text.Length
                        endPointer = whatToChange.GetPositionAtOffset(positionPointer + textlen)
                        Dim tr As TextRange = New TextRange(startPointer, endPointer)
                        changeRanges.Add(tr)
    
                        ' shift pointer by length of last text
                        positionPointer += textlen + 1
                    Else
                        ' get the next char pointer
                        positionPointer += 1
                    End If
                Catch ex As Exception
                    Exit While
                End Try
    
                previousPointerContext = thePointerContext
            End While
            Return changeRanges
    
        End Function

    Get selection TextRanges

    Public Function GetSelectedTextRanges(rtb As RichTextBox) As List(Of TextRange)
            Dim changeRanges As New List(Of TextRange)
    
            'If rtb.Selection.IsEmpty Then
            '    Return changeRanges
            '    Exit Function
            'End If
    
            Dim startPointer As TextPointer
            Dim endPointer As TextPointer
    
            Dim thePointerContext As TextPointerContext
            Dim previousPointerContext As TextPointerContext
    
            Dim whatToChange As TextPointer = rtb.Selection.Start
            Dim selectionLength As Integer = 0
    
    
            Dim selectionText As String = rtb.Selection.Text
    
            ' get rid of List extra characters because the FlowDocument API is 
            ' really screwy at this point 
            ' the function GetTextInRun shows the text while the text range has 
            ' extra chars for things like ListItem and TableCell
            ' we remove the extraneous chars to make the lenght of the selected items
            ' to match what is in the Run
    
            selectionText = selectionText.Replace(Chr(149) & vbTab, String.Empty)
    
            ' get rid of TableCell extra characters
            selectionText = selectionText.Replace(vbTab, String.Empty)
            selectionText = selectionText.Replace(vbCrLf, String.Empty)
    
            ' now set the selection length
    
            selectionLength = selectionText.Length
    
            'whatToChange = rtb.Selection.Start
    
            Dim positionPointer As Integer = 0
            While selectionLength > 0
                startPointer = whatToChange.GetPositionAtOffset(positionPointer)
                Try
                    thePointerContext = startPointer.GetPointerContext(LogicalDirection.Forward)
                    If (thePointerContext = TextPointerContext.Text And previousPointerContext = TextPointerContext.ElementStart) Or
                        (thePointerContext = TextPointerContext.Text And positionPointer = 0) Then
                        Dim text = startPointer.GetTextInRun(LogicalDirection.Forward)
                        Dim textlen As Integer = text.Length
    
                        If textlen > selectionLength Then
                            textlen = selectionLength
                            Dim t As Integer = 1
                        End If
                        endPointer = whatToChange.GetPositionAtOffset(positionPointer + textlen)
                        Dim tr As TextRange = New TextRange(startPointer, endPointer)
                        changeRanges.Add(tr)
                        selectionLength -= textlen
    
                        ' shift pointer by length of last text
                        positionPointer += textlen + 1
    
                    Else
                        ' get the next char pointer
                        positionPointer += 1
    
                    End If
                Catch ex As Exception
                    Exit While
                End Try
    
                previousPointerContext = thePointerContext
            End While
            Return changeRanges
    
        End Function


    Lloyd Sheen

    • Marked as answer by sqlguy Monday, August 26, 2019 5:19 PM
    Monday, August 26, 2019 3:50 PM