代码之家  ›  专栏  ›  技术社区  ›  Jason Kresowaty

检测流程文档更改并滚动

  •  5
  • Jason Kresowaty  · 技术社区  · 15 年前

    我想检测(最好通过事件)在 FlowDocument 当它发生时,我想引起 FlowDocumentScrollViewer 显示 流程文档 自动滚动到末尾。

    4 回复  |  直到 9 年前
        1
  •  7
  •   Ray Burns    15 年前

    您可以检测 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() 流文档滚动查看器 然后计算 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
  •  2
  •   Drew Marsh    15 年前

    是否使用RichTextBox进行编辑?如果是这样的话,你应该能够钩住 the TextChanged event 然后打电话 the ScrollToVerticalOffset method 值来自 the ViewportHeight property .

        3
  •  2
  •   Michael Sutton    12 年前

    在连接到一个TextChanged事件之后,您可以简单地使用:

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

    但是, 这作品 只有 在FlowDocumentPageViewer上, 也可以在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();
    
        4
  •  0
  •   Javier G.    9 年前

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

    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()即可。

    推荐文章