でコンテンツが追加、変更されたFlowDocument
ときなどを (できればイベントを通じて) 検出しFlowDocumentScrollViewer
たいと思いFlowDocument
ます。
4 に答える
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();
});
};
ここで、指定された型で見つかった最初の Visual を使用して返す、FindFirstVisualDescendantOfType
ビジュアル ツリーの単純な深さ優先プレフィックス検索です。VisualTreeHelper.GetChildrenCount()
VisualTreeHelper.GetChild()
FlowDocumentScrollViewer
のテンプレートは変更される可能性があるため、完全な一般性のために、コードの上部で scrollViewer を事前計算しないことに注意してください。これが起こらない場合は、 を呼び出し.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
RichTextBoxを使用して編集を行っていますか?その場合は、TextChanged
イベントをフックしてから、プロパティの値を使用してScrollToVerticalOffset
メソッドを呼び出すことができるはずです。ViewportHeight
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();
次の拡張メソッドを使用して、内部スクロール ビューアーを取得できます。
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() を呼び出すだけです。