Подтвердить что ты не робот

Как прокручивать в нижней части ScrollViewer автоматически с помощью Xaml и привязки?

У меня есть TextBlock, контент которого связан с строковым свойством ViewModel. Этот TextBlock имеет ScrollViewer, обернутый вокруг него.

То, что я хочу делать, это каждый раз, когда журналы меняются, ScrollViewer будет прокручиваться в нижнюю часть. В идеале я хочу что-то вроде этого:

    <ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto"
                  ScrollPosition="{Binding Path=ScrollPosition}">
        <TextBlock Text="{Binding Path=Logs}"/>
    </ScrollViewer>

Я не хочу использовать Code Behind! Решение, которое я ищу, должно использовать привязку только и/или Xaml.

4b9b3361

Ответ 1

Вы можете создать прикрепленное свойство или поведение, чтобы достичь того, чего вы хотите, без использования кода. В любом случае вам все равно придется писать код.

Вот пример использования прикрепленного свойства.

Прикрепленное свойство

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();
        }
    }
}

привязка Xaml

<ScrollViewer local:Helper.AutoScroll="{Binding IsLogsChangedPropertyInViewModel}" .../>

Вам нужно будет создать логическое свойство IsLogsChangedPropertyInViewModel и установить его в true при изменении свойства строки.

Надеюсь, это поможет!:)

Ответ 2

Отвеченный обновленный 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.SizeChanged += 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);
    }
}

Использование:

<ScrollViewer n:AutoScrollBehavior.AutoScroll="True" > // Where n is the XML namespace 

Ответ 3

Из Блог Джеффа в стиле прокрутки ScrollViewer.

Добавьте этот класс:

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);
            }
        }
    }
}

Этот код зависит от Blend Behaviors, для которого требуется ссылка на System.Windows.Interactivity. См. помощь при добавлении System.Windows.Interactivity.

Если вы устанавливаете пакет MVVM Light NuGet, вы можете добавить ссылку здесь:

packages\MvvmLightLibs.4.2.30.0\lib\net45\System.Windows.Interactivity.dll

Убедитесь, что у вас есть это свойство в заголовке, которое указывает на System.Windows.Interactivity.dll:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

Добавьте Blend Behavior в ScrollViewer:

<i:Interaction.Behaviors>
    <implementation:AutoScrollBehavior />
</i:Interaction.Behaviors>

Пример:

<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> 

Мы должны добавить определение для пространства имен, иначе он не будет знать, где найти только что добавленный класс С#. Добавьте это свойство в тег <Window>. Если вы используете ReSharper, он автоматически предложит вам это.

xmlns:implementation="clr-namespace:MyAttachedBehaviors"

Теперь, если все пойдет хорошо, текст в поле всегда будет прокручиваться вниз.

Приведенный пример XAML напечатает содержимое связанного свойства LogText на экране, которое идеально подходит для ведения журнала.

Ответ 4

Это легко, примеры:

yourContronInside.ScrollOwner.ScrollToEnd (); 
yourContronInside.ScrollOwner.ScrollToBottom ();

Ответ 5

Вот небольшая вариация.

Это будет прокручиваться донизу как при изменении высоты просмотра (видовой области) прокрутки, так и высоты ее прокрутки содержимого (степени) ведущего.

Это основано на ответе Роя Т, но я не смог прокомментировать, поэтому я опубликовал его как ответ.

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


        public static void AutoScrollPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var scrollViewer = obj as ScrollViewer;
            if (scrollViewer == null) return;

            if ((bool) args.NewValue)
            {
                scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
                scrollViewer.ScrollToEnd();
            }
            else
            {
                scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged;
            }
        }

        static void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            // Remove "|| e.ViewportHeightChange < 0 || e.ExtentHeightChange < 0" if you want it to only scroll to the bottom when it increases in size
            if (e.ViewportHeightChange > 0 || e.ExtentHeightChange > 0 || e.ViewportHeightChange < 0 || e.ExtentHeightChange < 0)
            {
                var scrollViewer = sender as ScrollViewer;
                scrollViewer?.ScrollToEnd();
            }
        }

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

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

Ответ 6

Я использовал @Roy T. ответ, однако я хотел добавить условие, что если вы прокручивались назад во времени, но затем добавил текст, прокрутка должна автоматически прокручивать вниз.

Я использовал это:

private static void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    var scrollViewer = sender as ScrollViewer;

    if (e.ExtentHeightChange > 0)
    {
        scrollViewer.ScrollToEnd();
    }    
}

вместо события SizeChanged.