Search code examples
c#wpfwpf-controlsflowdocumentflowdocumentscrollviewer

WPF Scroll To End of FlowDocument


I'm having trouble with getting a FlowDocument wrapped in a FlowDocumentScrollViewer to scroll to the end when its data changes.

This is my XAML code

<core:CustomFlowDocumentScrollViewer x:Name="ScrollViewer" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
    <FlowDocumentScrollViewer.Document>
        <FlowDocument PagePadding="0">
            <Paragraph Name="Paragraph"></Paragraph>
        </FlowDocument>
    </FlowDocumentScrollViewer.Document>
</core:CustomFlowDocumentScrollViewer>

The core:CustomFlowDocumentScrollViewer implements the following snippet https://stackoverflow.com/a/561319/13567181, just so I can call ScrollToBottom() later.

In my code-behind I'm clearing the Paragraph and adding new lines to it

private void PopulateFlowDocument(IEnumerable<LoggingEvent> list)
{
    Paragraph.Inlines.Clear();

    foreach (var loggingEvent in list)
    {
        var parsedRun = FormatLoggingEvent(loggingEvent);
        Paragraph.Inlines.Add(parsedRun);
        Paragraph.Inlines.Add(Environment.NewLine);
    }
}

Once PopulateFlowDocument completes, I call ScrollToEnd - The control performs some level of scrolling but does not work reliably. My datasource always returns 5000 rows but the scroll view only scrolls to line ~3750 (sometimes more, sometimes less).

Does FlowDocument work asynchronously internally???

Approach 2

Apart from the solutions available on SO, I've also tried the following suggestion from the MSDN forum.

void paragraph_Loaded(object sender, RoutedEventArgs e)
{
     Paragraph paragraph = (Paragraph)sender;
     paragraph.Loaded -= paragraph_Loaded;
     paragraph.BringIntoView();
} 

Like above, the Loaded event triggers to early and hence the scrolling does not work reliably.

What I'm looking for is an event/notification when the entire document has been updated so that I can reliably scroll to its very bottom.


Solution

  • I ended up achieving what I wanted using the dispatcher. I borrowed the idea from here

    Dispatcher.Invoke(new Action(() =>
    {
        SearchResultTextBox.ScrollToEnd();
    }), DispatcherPriority.ContextIdle, null);           
    

    It seems that the FlowDocument is using the dispatcher too and so the scroll needs to be low-priority enough so that its scheduled after the actual document modifications.