10

I have a FlowDocumentScrollViewer I want to automatically scroll to the bottom when text is added.

<FlowDocumentScrollViewer Name="Scroller">
 <FlowDocument Foreground="White" Name="docDebug" FontFamily="Terminal">
  <Paragraph Name="paragraphDebug"/>
 </FlowDocument>
</FlowDocumentScrollViewer>

In code I add Inlines to the Paragraph, but when there is to much text I would like to be able to simply scroll down using code instead of having the user doing so.

Any suggestions?

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
TimothyP
  • 21,178
  • 26
  • 94
  • 142

5 Answers5

15

try:

Scroller.ScrollViewer.ScrollToEnd();

Where "Scroller" is the name of your FlowDocumentScrollViewer.

EDIT: I wrote this answer a little too quickly. FlowDocumentScrollViewer does not expose a ScrollViewer property. I had actually extended the FlowDocumentScrollViewer class and implemented the ScrollViewer property myself. Here is the implementation:

  /// <summary>
  /// Backing store for the <see cref="ScrollViewer"/> property.
  /// </summary>
  private ScrollViewer scrollViewer;

  /// <summary>
  /// Gets the scroll viewer contained within the FlowDocumentScrollViewer control
  /// </summary>
  public ScrollViewer ScrollViewer
  {
     get
     {
        if (this.scrollViewer == null)
        {
           DependencyObject obj = this;

           do
           {
              if (VisualTreeHelper.GetChildrenCount(obj) > 0)
                 obj = VisualTreeHelper.GetChild(obj as Visual, 0);
              else
                 return null;
           }
           while (!(obj is ScrollViewer));

           this.scrollViewer = obj as ScrollViewer;
        }

        return this.scrollViewer;
     }
  }
John Myczek
  • 12,076
  • 4
  • 31
  • 44
11

I've faced a similar problem: I wanted a textual area which could hold my text, is able to wrap it, it fills its parent control and is scrollable.

First I've tried to use a TextBlock with a ScrollViewer and I think it worked, but for some reason I've wanted to use a FlowDocument instead with a FlowDocumentScrollViewer. This latter didn't work and I just couldn't leave the fight unattented so I tried to find solutions and this is how I got here. I've tried to apply the workarounds presented in the answers to the original question, however neither solutions worked out for me (I'm using .NET 4.5, maybe it works in other versions, but I don't know about that).

I've tried using a single FlowDocument by itself also, but the control contains some UI elements I didn't want. So, I came up with another solution.

  <ScrollViewer VerticalScrollBarVisibility="Auto">
    <FlowDocumentScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
      <FlowDocument>

That's right. It works! Calling ScrollViewer.ScrollToBottom() just works! The ScrollViewer enables scrolling and FlowDocumentScrollViewer removes the UI elements from the FlowDocument. Hope it helps!


Apparently my construction had a flaw, because this way the FlowDocument isn't scrollable via a mouse's scrolling wheel. However setting the FlowDocumentScrollViewer control's IsHitTestVisible property to False solves this.

Kohányi Róbert
  • 9,791
  • 4
  • 52
  • 81
8

The other answers given here are a bit puzzling, since I don't see any public "ScrollViewer" property on the FlowDocumentScrollViewer.

I hacked around the problem like this. Beware that this method can return null during initialization:

public static ScrollViewer FindScrollViewer(this FlowDocumentScrollViewer flowDocumentScrollViewer)
{
    if (VisualTreeHelper.GetChildrenCount(flowDocumentScrollViewer) == 0)
    {
        return null;
    }

    // Border is the first child of first child of a ScrolldocumentViewer
    DependencyObject firstChild = VisualTreeHelper.GetChild(flowDocumentScrollViewer, 0);
    if (firstChild == null)
    {
        return null;
    }

    Decorator border = VisualTreeHelper.GetChild(firstChild, 0) as Decorator;

    if (border == null)
    {
        return null;
    }

    return border.Child as ScrollViewer;
}
Anthony
  • 5,176
  • 6
  • 65
  • 87
  • Thanks for pointing out the error in my answer. I corrected it. – John Myczek Apr 01 '09 at 18:21
  • Note this FindScrollViewer method does not currently work, the version in John Myczek's answer does however. – Niall Jul 16 '10 at 23:59
  • By "currently", do you mean in WPF 4.0? Looking at it again, it could be fragile and broken by changes to the exact controls that lie between the FlowDocumentScrollViewer and the ScrollViewer caused by version or styling, so a simpler and more ignorant approach may be better. – Anthony Jul 17 '10 at 10:17
4

This may be a very late answer, but I've found a way to do this.

//after your FlowDocumentScrollViewer(for example, x:Name="fdsv") loaded
ScrollViewer sv = fdsv.Template.FindName("PART_ContentHost", fdsv) as ScrollViewer;

sv.ScrollToBottom();
sv.ScrollToTop();
sv.ScrollToVerticalOffset(100);
// etc.

Check IScrollInfo and ScrollViewer for details.

I hope this helps you.

Masaki Ohashi
  • 421
  • 4
  • 4
  • 1
    This should be higher up! It's much more straightforward than the accepted answer and works great! – Mako Mar 10 '23 at 21:52
2

This question was asked 7 years ago, now I have the same problem, and I find a simple solution. The follow code add a Section to Flowdocument which same to Paragraph, then scroll to the end.

private void addSection(Section section)
{
    section.Loaded += section_Loaded;
    fdoc.Blocks.Add(section);
}

private void section_Loaded(object sender, RoutedEventArgs e)//scroll to end
{
    var sec = sender as Section;
    if (sec != null)
    {
        sec.BringIntoView();
    }
}
chengzi
  • 170
  • 2
  • 10