如何使用Xaml和绑定自动滚动到ScrollViewer的底部?

JiB*_*evé 36 c# wpf xaml

我有一个TextBlock内容是绑定到ViewModel的字符串属性的数据.这TextBlock有一个ScrollViewer缠绕它.

我想要做的是每次日志更改时,ScrollViewer将滚动到底部.理想情况下我想要这样的东西:

    <ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto"
                  ScrollPosition="{Binding Path=ScrollPosition}">
        <TextBlock Text="{Binding Path=Logs}"/>
    </ScrollViewer>
Run Code Online (Sandbox Code Playgroud)

希望后面使用代码!我在寻找应该使用该解决方案唯一的结合和/或XAML中.

Jus*_* XL 45

您可以创建附加属性或行为来实现您想要的而不使用后面的代码.无论哪种方式,您仍然需要编写一些代码.

以下是使用附加属性的示例.

附属物

public static class Helper
{
    public static bool GetAutoScroll(DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoScrollProperty);
    }

    public static void SetAutoScroll(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoScrollProperty, value);
    }

    public static readonly DependencyProperty AutoScrollProperty =
        DependencyProperty.RegisterAttached("AutoScroll", typeof(bool), typeof(Helper), new PropertyMetadata(false, AutoScrollPropertyChanged));

    private static void AutoScrollPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var scrollViewer = d as ScrollViewer;

        if (scrollViewer != null && (bool)e.NewValue)
        {
            scrollViewer.ScrollToBottom();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Xaml绑定

<ScrollViewer local:Helper.AutoScroll="{Binding IsLogsChangedPropertyInViewModel}" .../>
Run Code Online (Sandbox Code Playgroud)

您需要创建一个布尔属性,IsLogsChangedPropertyInViewModel并在更改字符串属性时将其设置为true.

希望这可以帮助!:)


Roy*_* T. 26

答案更新2017-12-13,现在使用ScrollChanged事件并检查范围的大小是否发生变化.更可靠,不会干扰手动滚动

我知道这个问题很老,但我有一个改进的实现:

  • 没有外部依赖
  • 您只需要设置一次属性

该代码深受Justin XL和Contango解决方案的影响

public static class AutoScrollBehavior
{
    public static readonly DependencyProperty AutoScrollProperty =
        DependencyProperty.RegisterAttached("AutoScroll", typeof(bool), typeof(AutoScrollBehavior), new PropertyMetadata(false, AutoScrollPropertyChanged));


    public static void AutoScrollPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var scrollViewer = obj as ScrollViewer;
        if(scrollViewer != null && (bool)args.NewValue)
        {
            scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
            scrollViewer.ScrollToEnd();
        }
        else
        {
            scrollViewer.ScrollChanged-= ScrollViewer_ScrollChanged;
        }
    }

    private static void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        // Only scroll to bottom when the extent changed. Otherwise you can't scroll up
        if (e.ExtentHeightChange != 0)
        {
            var scrollViewer = sender as ScrollViewer;
            scrollViewer?.ScrollToBottom();
        }
    }

    public static bool GetAutoScroll(DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoScrollProperty);
    }

    public static void SetAutoScroll(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoScrollProperty, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

<ScrollViewer n:AutoScrollBehavior.AutoScroll="True" > // Where n is the XML namespace 
Run Code Online (Sandbox Code Playgroud)

  • @RoyT在`AutoScrollPropertyChanged`方法中,当`scrollViewer`为null时,可以到达`else`,不仅当`scrollViewer`不是'null`而且`NewValue`是'false`时 (3认同)

Con*_*ngo 12

来自Geoff的ScrollViewer AutoScroll行为博客.

添加此课程:

namespace MyAttachedBehaviors
{
    /// <summary>
    ///     Intent: Behavior which means a scrollviewer will always scroll down to the bottom.
    /// </summary>
    public class AutoScrollBehavior : Behavior<ScrollViewer>
    {
        private double _height = 0.0d;
        private ScrollViewer _scrollViewer = null;

        protected override void OnAttached()
        {
            base.OnAttached();

            this._scrollViewer = base.AssociatedObject;
            this._scrollViewer.LayoutUpdated += new EventHandler(_scrollViewer_LayoutUpdated);
        }

        private void _scrollViewer_LayoutUpdated(object sender, EventArgs e)
        {
            if (Math.Abs(this._scrollViewer.ExtentHeight - _height) > 1)
            {
                this._scrollViewer.ScrollToVerticalOffset(this._scrollViewer.ExtentHeight);
                this._height = this._scrollViewer.ExtentHeight;
            }
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            if (this._scrollViewer != null)
            {
                this._scrollViewer.LayoutUpdated -= new EventHandler(_scrollViewer_LayoutUpdated);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码取决于Blend Behaviors,它需要引用System.Windows.Interactivity.请参阅添加帮助System.Windows.Interactivity.

如果您安装MVVM Light NuGet包,可以在此处添加引用:

packages\MvvmLightLibs.4.2.30.0\lib\net45\System.Windows.Interactivity.dll
Run Code Online (Sandbox Code Playgroud)

确保标题中包含此属性,该属性指向System.Windows.Interactivity.dll:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Run Code Online (Sandbox Code Playgroud)

将混合行为添加到ScrollViewer:

<i:Interaction.Behaviors>
    <implementation:AutoScrollBehavior />
</i:Interaction.Behaviors>
Run Code Online (Sandbox Code Playgroud)

例:

<GroupBox Grid.Row="2" Header ="Log">
    <ScrollViewer>
        <i:Interaction.Behaviors>
            <implementation:AutoScrollBehavior />
        </i:Interaction.Behaviors>
        <TextBlock Margin="10" Text="{Binding Path=LogText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextWrapping="Wrap"/>
    </ScrollViewer>
</GroupBox> 
Run Code Online (Sandbox Code Playgroud)

我们必须为命名空间添加一个定义,否则它将不知道在哪里找到我们刚刚添加的C#类.将此属性添加到<Window>标记中.如果您使用的是ReSharper,它会自动为您提供此信息.

xmlns:implementation="clr-namespace:MyAttachedBehaviors"
Run Code Online (Sandbox Code Playgroud)

现在,如果一切顺利,框中的文本将始终向下滚动到底部.

给出的示例XAML将绑定属性的内容打印LogText到屏幕,这非常适合日志记录.


小智 5

这很简单,例如:

yourContronInside.ScrollOwner.ScrollToEnd (); 
yourContronInside.ScrollOwner.ScrollToBottom ();
Run Code Online (Sandbox Code Playgroud)