Click here to Skip to main content
15,898,588 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
What I'm doing here is loading a file into a RichTextBox as an XamlPackage. Once loaded, I calculate the number of lines per page (NoLinesPerPage) (from preset top/bottom margins and target paper height).
The particular values I'm working with give me a number of 36 lines per page.

Now, I want to draw lines at soft page breaks (every 36 lines), so I'm trying to get the GetCharacterRect(LogicalDirection.Forward).Y values for the first character of every 36 lines (Y values which I then pass to a set of RichTextBox Adorners), to the end of the FlowDocument.

The problem is, when I load my FlowDocument and run the subroutine to go through every 36 lines, I'm not getting them all the way through to the end. Only about the first 75% or so.

My routine for getting the TextPointers at each 36th line is thus:

VB
Private Sub UpdatePageLinePositions()

        Dim PageLinePointers As New List(Of TextPointer)

        Dim tpointer As TextPointer = Dox.Document.ContentStart.GetLineStartPosition(0)
        Dim ypoint As Integer = 0
        Dim charrect As Rect = Nothing
        Dim nextpagept As TextPointer = tpointer
        Dim lastpage As Boolean = False

        While lastpage = False

           Dim pcount As Integer = 0
           nextpagept = nextpagept.GetLineStartPosition(CurrDoc.NoLinesPerPage, pcount)

            If pcount = CurrDoc.NoLinesPerPage Then
               'MsgBox(pcount)
                PageLinePointers.Add(nextpagept)
            Else
                lastpage = True
            End If

        End While

        MsgBox(PageLinePointers.Count)
        For Each plp As TextPointer In PageLinePointers
            charrect = plp.GetCharacterRect(LogicalDirection.Forward)
            AdornerLayer.GetAdornerLayer(Dox).Add(New LineAdorner(Dox, charrect.Y))
        Next plp


(I end the loop if pcount is less than the skipped number of lines (which means it is at the end of the document).
Now, for my test document, I should come up with 19 pages (18 adorners), but when I load the document I only get 12, or sometimes 13 (the last ~25% of the document has no visible adorners, and my PageLinePointers list count is only 12 or 13.

However, if I don't comment out the MsgBox(pcount) within the While loop, so that the value "36" gets messaged 18 times, then I get all 18 PageLinePointers, and all 18 page line adorners are visible on my FlowDocument.

What I have tried:

So, I guessed this must be a threading issue, it's looping too fast and reaching the end (lastpage=true) before the last few
nextpagept.GetLineStartPosition
calls are made, and the returned values actually get added to the List.

So I tried a running as a synchronous Task:

Dim mytask As New Task(Sub() nextpagept = nextpagept.GetLineStartPosition(CurrDoc.NoLinesPerPage, pcount))
            mytask.RunSynchronously()


inside the Loop, but it still stops at the 12th or 13th loop, failing to get all of the page line textpointers.

I also tried with a Dispatcher
Application.Current.Dispatcher.Invoke(Sub() nextpagept = nextpagept.GetLineStartPosition(CurrDoc.NoLinesPerPage, pcount))
with the same result.
But in all cases, if I have the interruption of a MsgBox inserted in the loop, it gets all 18 pages.

What am I failing to do correctly here?
Posted
Updated 9-Oct-18 4:06am

1 solution

Ok, with further tinkering I seem to have found the problem.
It seems it was not an issue of threading in my Sub, but rather the fact that I was running the sub UpdatePageLinePositions() immediately after loading the Flowdocument in the richtextbox, and perhaps because the flowdocument gets loaded asynchrously, not all of the lines are quite yet loaded by the time my Sub gets run.
Hence the incomplete number of lines/pages found.

So I've placed the Sub UpdatePageLinePositions() in the TextChanged event of the RichTextBox, which apparently fires only after all the lines have been loaded in, and all my lines get counted so all the Adorner Y positions get calculated.

Although, doing so makes the Sub get run after every text change, which is definitely not desirable. Ideally it would only run after the initial document load.
(Incidentally I tried
VB
While Not RichTextBox.Document.isLoaded:End While
, to get the document loaded point, but it seems that is still too early (still doesn't get all of the lines). So I've settled on TextChanged.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900