Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 973003
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 16, 20262026-05-16T03:12:53+00:00 2026-05-16T03:12:53+00:00

My goal is to create a reusable Attached Behavior for a FlowDocumentScrollViewer, so that

  • 0

My goal is to create a reusable Attached Behavior for a FlowDocumentScrollViewer, so that the viewer automaticly scrolls to the end whenever the FlowDocument has been updated (appended).

Problems so far:

  • OnEnabledChanged gets called before the visual tree is completed, and thus doesn’t find the ScrollViewer
  • I don’t know how to attach to the DependencyProperty containing the FlowDocument. My plan was to use it’s changed event to initialize the ManagedRange property. (Manually triggered for the first time if needed.)
  • I don’t know how to get to the ScrollViewer property from within the range_Changed method, as it doesn’t have the DependencyObject.

I realize that those are potentially 3 separate issues (aka. questions). However they are dependent on each other and the overall design I’ve attempted for this behavior. I’m asking this as a single question in case I’m going about this the wrong way. If I am, what is the right way?

/// Attached Dependency Properties not shown here:
///   bool Enabled
///   DependencyProperty DocumentProperty
///   TextRange MonitoredRange
///   ScrollViewer ScrollViewer

public static void OnEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d == null || System.ComponentModel.DesignerProperties.GetIsInDesignMode(d))
        return;

    DependencyProperty documentProperty = null;
    ScrollViewer scrollViewer = null;

    if (e.NewValue is bool && (bool)e.NewValue)
    {
        // Using reflection so that this will work with similar types.
        FieldInfo documentFieldInfo = d.GetType().GetFields().FirstOrDefault((m) => m.Name == "DocumentProperty");
        documentProperty = documentFieldInfo.GetValue(d) as DependencyProperty;

        // doesn't work.  the visual tree hasn't been built yet
        scrollViewer = FindScrollViewer(d);
    }

    if (documentProperty != d.GetValue(DocumentPropertyProperty) as DependencyProperty)
        d.SetValue(DocumentPropertyProperty, documentProperty);

    if (scrollViewer != d.GetValue(ScrollViewerProperty) as ScrollViewer)
        d.SetValue(ScrollViewerProperty, scrollViewer);
}

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

    return obj as ScrollViewer;
}

public static void OnDocumentPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.OldValue != null)
    {
        DependencyProperty dp = e.OldValue as DependencyProperty;
        // -= OnFlowDocumentChanged
    }

    if (e.NewValue != null)
    {
        DependencyProperty dp = e.NewValue as DependencyProperty;
        // += OnFlowDocumentChanged

        // dp.AddOwner(typeof(AutoScrollBehavior), new PropertyMetadata(OnFlowDocumentChanged));
        //   System.ArgumentException was unhandled by user code Message='AutoScrollBehavior' 
        //   type must derive from DependencyObject.
    }
}

public static void OnFlowDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    TextRange range = null;

    if (e.NewValue != null)
    {
        FlowDocument doc = e.NewValue as FlowDocument;

        if (doc != null)
            range = new TextRange(doc.ContentStart, doc.ContentEnd);
    }

    if (range != d.GetValue(MonitoredRangeProperty) as TextRange)
        d.SetValue(MonitoredRangeProperty, range);
}


public static void OnMonitoredRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.OldValue != null)
    {
        TextRange range = e.OldValue as TextRange;
        if (range != null)
            range.Changed -= new EventHandler(range_Changed);
    }

    if (e.NewValue != null)
    {
        TextRange range = e.NewValue as TextRange;
        if (range != null)
            range.Changed -= new EventHandler(range_Changed);
    }
}

static void range_Changed(object sender, EventArgs e)
{
    // need ScrollViewer!!
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-16T03:12:54+00:00Added an answer on May 16, 2026 at 3:12 am

    OnEnabledChanged gets called before
    the visual tree is completed, and thus
    doesn’t find the ScrollViewer

    Use Dispatcher.BeginInvoke to enqueue the rest of the work to happen asynchronously, after the visual tree is built. You will also need to call ApplyTemplate to ensure that the template has been instantiated:

    d.Dispatcher.BeginInvoke(new Action(() =>
    {
        ((FrameworkElement)d).ApplyTemplate();
        d.SetValue(ScrollViewerProperty, FindScrollViewer(d));
    }));
    

    Note that you don’t need to check whether the new value is different from the old one. The framework handles that for you when setting dependency properties.

    You could also use FrameworkTemplate.FindName to get the ScrollViewer from the FlowDocumentScrollViewer. FlowDocumentScrollViewer has a named template part of type ScrollViewer called PART_ContentHost that is where it will actually host the content. This can be more accurate in case the viewer is re-templated and has more than one ScrollViewer as a child.

    var control = d as Control;
    if (control != null)
    {
        control.Dispatcher.BeginInvoke(new Action(() =>
        {
            control.ApplyTemplate();
            control.SetValue(ScrollViewerProperty,
                control.Template.FindName("PART_ContentHost", control)
                    as ScrollViewer);
        }));
    }
    

    I don’t know how to attach to the
    DependencyProperty containing the
    FlowDocument. My plan was to use it’s
    changed event to initialize the
    ManagedRange property. (Manually
    triggered for the first time if
    needed.)

    There is no way built into the framework to get property changed notification from an arbitrary dependency property. However, you can create your own DependencyProperty and just bind it to the one you want to watch. See Change Notification for Dependency Properties for more information.

    Create a dependency property:

    private static readonly DependencyProperty InternalDocumentProperty = 
        DependencyProperty.RegisterAttached(
            "InternalDocument",
            typeof(FlowDocument),
            typeof(YourType),
            new PropertyMetadata(OnFlowDocumentChanged));
    

    And replace your reflection code in OnEnabledChanged with simply:

    BindingOperations.SetBinding(d, InternalDocumentProperty, 
        new Binding("Document") { Source = d });
    

    When the Document property of the FlowDocumentScrollViewer changes, the binding will update InternalDocument, and OnFlowDocumentChanged will be called.

    I don’t know how to get to the
    ScrollViewer property from within the
    range_Changed method, as it doesn’t
    have the DependencyObject.

    The sender property will be a TextRange, so you could use ((TextRange)sender).Start.Parent to get a DependencyObject and then walk up the visual tree.

    An easier method would be to use a lambda expression to capture the d variable in OnMonitoredRangeChanged by doing something like this:

    range.Changed += (sender, args) => range_Changed(d);
    

    And then creating an overload of range_Changed that takes in a DependencyObject. That will make it a little harder to remove the handler when you’re done, though.

    Also, although the answer to Detect FlowDocument Change and Scroll says that TextRange.Changed will work, I didn’t see it actually fire when I tested it. If it doesn’t work for you and you’re willing to use reflection, there is a TextContainer.Changed event that does seem to fire:

    var container = doc.GetType().GetProperty("TextContainer", 
        BindingFlags.Instance | BindingFlags.NonPublic).GetValue(doc, null);
    var changedEvent = container.GetType().GetEvent("Changed", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    EventHandler handler = range_Changed;
    var typedHandler = Delegate.CreateDelegate(changedEvent.EventHandlerType, 
        handler.Target, handler.Method);
    changedEvent.GetAddMethod(true).Invoke(container, new object[] { typedHandler });
    

    The sender parameter will be the TextContainer, and you can use reflection again to get back to the FlowDocument:

    var document = sender.GetType().GetProperty("Parent", 
        BindingFlags.Instance | BindingFlags.NonPublic)
        .GetValue(sender, null) as FlowDocument;
    var viewer = document.Parent;
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.