2009-10-31 62 views

回答

7

通过创建文本范围并监视变化,您可以检测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的模板可以更改。如果这不会发生,这个代码可以通过在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受保护。

+0

我不需要任何与FlowDocument和FlowDocumentScrollViewer可视化树代码。只需2次调用,1就可以创建并添加字符串中的段落,并将该段落添加到视图中。 – 2015-02-25 18:33:43

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(); 
+0

我只使用FlowDocument和FlowDocumentscrollViewer。上面的BringIntoView解决方案完全适合我。我不需要VisualTreeHelper 我的解决方案是使用Dispatcher.Invoke编写创建段落,将该段落存储为变量,然后再次将Dispatcher.Invoke放入视图中。 这似乎是最简单的方法。 – 2015-02-25 18:32:38

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

此外,您可以添加内容,检查滚动位置的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()为例。