Вы можете обнаружить изменения в FlowDocument
, создав текстовый диапазон и отслеживая его на предмет изменений. Прокрутить до конца сложнее, потому что вы должны найти ScrollViewer
. Кроме того, для повышения производительности не требуется повторять все расчеты прокрутки при каждом изменении, поэтому следует использовать DispatcherOperations
.
Собрав все воедино, этот код должен помочь:
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();
});
};
где FindFirstVisualDescendantOfType
- это простой поиск в глубине по префиксу визуального дерева с использованием VisualTreeHelper.GetChildrenCount()
и VisualTreeHelper.GetChild()
и возвращение первого найденного визуала указанного типа.
Обратите внимание, что для полной общности я не вычисляю scrollViewer в верхней части кода, потому что шаблон FlowDocumentScrollViewer
может измениться. Если этого не произойдет, этот код можно ускорить, вызвав .ApplyTemplate()
на FlowDocumentScrollViewer
, а затем вычислив scrollViewer
до регистрации обработчика события:
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();
});
};
Обратите внимание, что мы не можем просто вызвать scrollViewer.GetTemplateChild("PART_ContentHost")
и пропустить поиск по дереву, потому что GetTemplateChild
защищен.