You can detect changes in the FlowDocument by creating a text range and checking it for changes. Scrolling to the bottom is harder because you need to find the ScrollViewer . Also, for performance, you donโt need to redo all scrolls with every change, so you should use DispatcherOperations .
Combining all this, this code should do the trick:
var range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd); object operation = null; range.Changed += (obj, e) => { if(operation==null) operation = Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => { operation = null; var scrollViewer = FindFirstVisualDescendantOfType<ScrollViewer>(flowDocument); scrollViewer.ScrollToBottom(); }); };
where FindFirstVisualDescendantOfType is a simple preliminary depth search in the visual tree using VisualTreeHelper.GetChildrenCount() and VisualTreeHelper.GetChild() and returning the first visual found type.
Note that for complete generality, I will not precompute scrollViewer at the top of the code, because the FlowDocumentScrollViewer template may change. If this does not happen, this code can be accelerated by calling .ApplyTemplate() on the FlowDocumentScrollViewer , and then ScrollViewer before registering the event handler:
var range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd); object operation = null; flowDocument.ApplyTemplate(); var scrollViewer = FindFirstVisualDescendantOfType<ScrollViewer>(flowDocument); range.Changed += (obj, e) => { if(operation==null) operation = Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => { operation = null; scrollViewer.ScrollToBottom(); }); };
Note that we cannot just call scrollViewer.GetTemplateChild("PART_ContentHost") and skip the visual tree search because GetTemplateChild is protected.
Ray burns
source share