2009-10-31 18 views
5

FlowDocumentにコンテンツが追加、変更されたときなどを検出したい場合は(できればイベントを通じて)、FlowDocumentを表示しているFlowDocumentScrollViewerを自動的に最後までスクロールさせたい場合。FlowDocumentの検出と変更スクロール

答えて

7

FlowDocumentの変更は、テキスト範囲を作成して変更を監視することで検出できます。あなたがScrollViewerを見つけなければならないので、下にスクロールすることはより困難です。また、パフォーマンスのために、すべての変更ですべてのスクロール計算をやり直す必要はないので、DispatcherOperationsを使用してください。 FindFirstVisualDescendantOfTypeは、指定されたの見つけシンプルな深さ優先プレフィックスVisualTreeHelper.GetChildrenCount()VisualTreeHelper.GetChild()を使用してビジュアルツリーの検索や最初のビジュアルを返すです

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

:このコードは、トリックを行う必要がありますすべて一緒にそれを置く

タイプ。

FlowDocumentScrollViewerのテンプレートが変更される可能性があるため、完全性のために私はコードの先頭にscrollViewerを事前計算していません。これが起こらない場合は、このコードはFlowDocumentScrollViewer.ApplyTemplate()を呼び出し、その後、イベントハンドラが登録される前にscrollViewerを計算することによって高速化することができます。私たちは単にscrollViewer.GetTemplateChild("PART_ContentHost")を呼び出し、ビジュアルツリーの検索をスキップすることはできません

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

注意GetTemplateChildが保護されているためです。

+0

私はFlowDocumentとFlowDocumentScrollViewerのビジュアルツリーコードでこれを必要としませんでした。 Just 2 Invokes、1は文字列の段落を作成して追加し、1はその段落を表示します。 –

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を再度表示して表示することです。 これは最も簡単な方法です。 –

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の自分自身をチェックするためにコンテンツを追加する前に、これらの拡張メソッドを使用することができます - スクロール視聴者が例えば端部)にすでにだった場合のみ - :

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()を呼び出します。

関連する問題