programing

Xaml과 바인딩을 사용하여 ScrollViewer의 맨 아래까지 자동으로 스크롤하는 방법

showcode 2023. 4. 15. 09:38
반응형

Xaml과 바인딩을 사용하여 ScrollViewer의 맨 아래까지 자동으로 스크롤하는 방법

나 있어TextBlock이 컨텐츠는 ViewModel의 문자열 속성에 바인딩된 데이터입니다.이것.TextBlock가 있다ScrollViewer감쌌어요.

로그가 바뀔 때마다ScrollViewer아래로 스크롤합니다.이상적으로는 다음과 같은 것을 원합니다.

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

코드 비하인드를 사용하고 싶지 않아!원하는 솔루션은 바인딩 및/또는 Xaml만 사용해야 합니다.

코드 배후에 사용하지 않고 원하는 것을 달성하기 위해 연결된 속성 또는 동작을 만들 수 있습니다.어느 쪽이든 코드를 작성해야 합니다.

다음은 첨부된 속성을 사용하는 예입니다.

부속 재산

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로 설정합니다.

이것이 도움이 되기를! :)

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

사용방법:

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

ScrollViewer AutoScroll 동작에 관한 Geoff 블로그에서 확인.

다음 클래스 추가:

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. 추가 도움말을 참조하십시오.

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"

에 블렌드 동작 추가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> 

네임스페이스에 대한 정의를 추가해야 합니다.그렇지 않으면 방금 추가한 C# 클래스를 찾을 수 없습니다.이 속성을 에 추가합니다.<Window>태그. ReSharper를 사용하는 경우 자동으로 제안됩니다.

xmlns:implementation="clr-namespace:MyAttachedBehaviors"

모든 것이 잘 진행되면 상자의 텍스트가 항상 아래로 스크롤됩니다.

제공된 예 XAML은 바인딩된 속성의 내용을 인쇄합니다.LogText로그 작성에 최적인 화면입니다.

간단합니다. 예를 들어 다음과 같습니다.

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

여기 약간의 변화가 있습니다.

스크롤 뷰어 높이(뷰포트)와 스크롤 발표자의 콘텐츠 높이(확장)가 변경되면 아래로 스크롤합니다.

Roy T의 답변에 근거하고 있습니다만, 코멘트를 할 수 없어서 답변으로 올렸습니다.

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

저는 @Roy T.의 답변을 사용하고 있었습니다만, 시간을 거슬러 스크롤하면 스크롤 뷰가 자동으로 아래로 스크롤된다는 조항을 추가하고 싶었습니다.

저는 이걸 썼어요

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

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

SizeChanged 이벤트 대신 사용됩니다.

언급URL : https://stackoverflow.com/questions/8370209/how-to-scroll-to-the-bottom-of-a-scrollviewer-automatically-with-xaml-and-bindin

반응형