6

我想检测(最好通过一个事件)何时在 a 中添加、更改等任何内容,FlowDocument并且当它发生时,我想让 aFlowDocumentScrollViewer显示FlowDocument自动滚动到末尾。

4

4 回答 4

10

您可以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()此代码FlowDocumentScrollViewerscrollViewer

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它是受保护的。

于 2009-11-19T00:48:13.010 回答
2

您是否使用 RichTextBox 进行编辑?如果是这样,您应该能够挂钩事件,然后使用TextChanged属性ScrollToVerticalOffset值调用方法ViewportHeight

于 2009-11-01T16:25:07.650 回答
2

连接到 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(带有页面 ViewingModes),对于 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();
于 2012-11-23T10:49:53.070 回答
0

您可以使用以下扩展方法来获取内部滚动查看器:

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;
  }
}

此外,您可以在添加内容之前使用这些扩展方法来检查滚动查看器本身的滚动位置(如果您想要滚动 - 仅 - 如果滚动查看器已经位于末尾):

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() 例如。

于 2016-01-24T00:34:31.693 回答