Autoscroll Textbox if user has not moved the scrol

2019-09-04 07:25发布

问题:

Is it possible to let a WPF-TextBox autoscroll but stop that behavior when the ScrollBar is moved. When the user scrolls to end the TextBox should scroll again automatically to end on text change.

textBox1.ScrollToEnd();

This would work as long as the user does not move the ScrollBar.

回答1:

I have recreated what I think you have in your setup:

<StackPanel>
    <ScrollViewer Height="50" x:Name="_scroll">
        <TextBox x:Name="_text" Width="50" Text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."  TextWrapping="Wrap"/>
    </ScrollViewer>
    <Button Click="Button_Click" Content="Add Text"/>
</StackPanel>

I assume that you have some event that edits this textbox in my setup it is a button. And its click handler:

private void Button_Click(object sender, RoutedEventArgs e)
    {
        var shouldScroll = _scroll.VerticalOffset == _scroll.ScrollableHeight;

        _text.Text += "AAA ";

        if (shouldScroll)
        {
            _scroll.ScrollToEnd();
        }
    }

This will auto scroll to end after text edit only if scrollbar is on the bottom.

Edit

For cleaner solution you can use behavior:

  public class AutoScrollingBehavior : Behavior<ScrollViewer>
{
    public object UpdateTrigger
    {
        get { return (object)GetValue(UpdateTriggerProperty); }
        set { SetValue(UpdateTriggerProperty, value); }
    }

    public static readonly DependencyProperty UpdateTriggerProperty =
        DependencyProperty.Register("UpdateTrigger", typeof(object), typeof(AutoScrollingBehavior), new UIPropertyMetadata(Update));

    private bool IsScrolledDown
    {
        get { return (bool)GetValue(IsScrolledDownProperty); }
        set { SetValue(IsScrolledDownProperty, value); }
    }

    public static readonly DependencyProperty IsScrolledDownProperty =
        DependencyProperty.Register("IsScrolledDown", typeof(bool), typeof(AutoScrollingBehavior), new UIPropertyMetadata(false));

    private static void Update(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)d.GetValue(IsScrolledDownProperty))
        {
            var scroll = ((AutoScrollingBehavior)d).AssociatedObject;
            scroll.ScrollToEnd();
        }
    }

    protected override void OnAttached()
    {
        AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
        AssociatedObject.ScrollChanged += new ScrollChangedEventHandler(AssociatedObject_ScrollChanged);
    }

    private void AssociatedObject_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        IsScrolledDown = AssociatedObject.VerticalOffset == AssociatedObject.ScrollableHeight;
    }

    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        IsScrolledDown = AssociatedObject.VerticalOffset == AssociatedObject.ScrollableHeight;
    }
}

and xaml:

<StackPanel>
    <ScrollViewer Height="50" >
        <e:Interaction.Behaviors>
            <local:AutoScrollingBehavior UpdateTrigger="{Binding ElementName=_text,Path=Text}" />
        </e:Interaction.Behaviors>
        <TextBox x:Name="_text" Width="50" Text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."  TextWrapping="Wrap"/>
</ScrollViewer>
    <Button Click="Button_Click" Content="Add Text"/>
</StackPanel>


回答2:

I found a somehow hacky way to do this:

public class AutoScrollTextBox : TextBox
{
    ScrollViewer sv;
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        if (Template != null)
        {
            sv = (ScrollViewer)Template.FindName("PART_ContentHost", this);
        }
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);
        if (sv != null && sv.ScrollableHeight == sv.VerticalOffset)
        {
            this.ScrollToEnd();
        }
    }
}

But this would stop working if the name of the ScrollViewer in the TextBox-Template changes.