Обнаружение изменения FlowDocument и прокрутка - PullRequest
5 голосов
/ 31 октября 2009

Я хочу определить (предпочтительно через событие), когда какой-либо контент добавляется, изменяется и т. Д. В FlowDocument, и когда это требуется, я хочу, чтобы FlowDocumentScrollViewer, отображающий FlowDocument, автоматически прокручивал до конец.

Ответы [ 4 ]

7 голосов
/ 19 ноября 2009

Вы можете обнаружить изменения в 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 защищен.

2 голосов
/ 23 ноября 2012

После подключения к событию TextChanged вы можете просто использовать:

// Showing Last Block
YourReader.Document.Blocks.LastBlock.BringIntoView();

// Or.. showing the last Inline
(YourReader.Document.Blocks.LastBlock as Paragraph).Inlines.LastInline.BringIntoView();

Но, это работает только в FlowDocumentPageViewer, а также в FlowDocumentReader (с режимами просмотра страниц) для FlowDocumentScrollViewer Вы должны использовать визуальное дерево, как упомянуто

public static ScrollViewer FindScroll(Visual visual)
        {
            if (visual is ScrollViewer)
                return visual as ScrollViewer;

            ScrollViewer searchChiled = null;
            DependencyObject chiled;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
            {
                chiled = VisualTreeHelper.GetChild(visual, i);
                if (chiled is Visual)
                    searchChiled = FindScroll(chiled as Visual);
                if (searchChiled != null)
                    return searchChiled;
            }

            return null;
        }

ScrollViewer scroller = FindScroll(YourReader as Visual);
if (scroller != null) 
   (scroller as ScrollViewer).ScrollToBottom();
2 голосов
/ 01 ноября 2009

Вы используете RichTextBox для редактирования? Если это так, вы просто сможете перехватить событие TextChanged и затем вызвать метод ScrollToVerticalOffset со значением из свойства ViewportHeight .

0 голосов
/ 24 января 2016

Вы можете использовать следующий метод расширения, чтобы получить внутренний просмотрщик прокрутки:

public static class FlowDocumentScrollViewerExtensions
{
  public static ScrollViewer GetScrollViewer(this FlowDocumentScrollViewer element) {
    if (element == null) {
      throw new ArgumentNullException(nameof(element));
    }

    return element.Template?.FindName("PART_ContentHost", element) as ScrollViewer;
  }
}

Кроме того, вы можете использовать эти методы расширения перед добавлением контента, чтобы проверить положение прокрутки самого прокрутки (например, если вы хотите прокрутить -only-, если просмотрщик прокрутки уже был в конце):

public static class ScrollViewerExtensions
{
  public static bool IsAtHome(this ScrollViewer element) {
    if (element == null) {
      throw new ArgumentNullException(nameof(element));
    }

    return element.VerticalOffset <= 0;
  }

  public static bool IsAtEnd(this ScrollViewer element) {
    if (element == null) {
      throw new ArgumentNullException(nameof(element));
    }

    return element.VerticalOffset >= element.ScrollableHeight;
  }
}

После этого просто вызовите scrollViewer.ScrollToEnd (), например.

...