Best Way to Scroll to End of ScrollViewer

2020-06-09 07:58发布

Currently, the only way I have gotten my code to auto scroll to the end when I add a new item is the following:

XAML:

<ScrollViewer x:Name="chatViewScroller" HorizontalAlignment="Left" Height="201" Margin="0,32,0,0" VerticalAlignment="Top" Width="475" Background="#7FFFFFFF">
    <StackPanel x:Name="chatViewContent" />
</ScrollViewer>

Code:

                chatViewContent.Children.Add(
                    new TextBlock() {
                        Text = text,
                        FontSize = 18,
                        TextWrapping = Windows.UI.Xaml.TextWrapping.Wrap,
                        Margin = new Thickness(10, 3, 10, 0),
                        Foreground = new Windows.UI.Xaml.Media.SolidColorBrush(
                            isServerMessage ? Windows.UI.Colors.Purple : Windows.UI.Colors.Black)
                    });
                await Task.Delay(10);
                chatViewScroller.ScrollToVerticalOffset(chatViewScroller.ScrollableHeight);

Is this the accepted way of doing it? Do I have to wait for some random period of time?

9条回答
做自己的国王
2楼-- · 2020-06-09 08:03

Using ActualHeight did not work for me (I have yet to figure out why) - but using ScrollableHeight like this did the the trick:

// adding item to ItemsControl...
// ...
_scrollViewer.UpdateLayout();
_scrollViewer.ScrollToVerticalOffset(_scrollViewer.ScrollableHeight);
查看更多
放荡不羁爱自由
3楼-- · 2020-06-09 08:04

I've used the following:

ScrollViewer viewer = GetScrollViewer();
if (viewer != null)
{
    viewer.ScrollToVerticalOffset(viewer.ActualHeight);
}

Another solution is to put the items into a ListBox and use the ScrollIntoView method

查看更多
唯我独甜
4楼-- · 2020-06-09 08:13

In my opinion best option is to inherit from ScrollViewer in following way:

public partial class AutoScrollViewer
{        
    public AutoScrollViewer()
    {
        InitializeComponent();
        this.SizeChanged += (sender, args) => this.ScrollToBottom();
    }
}

and XAML

<ScrollViewer x:Class="AutoScrollViewer"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</ScrollViewer>

Then you can use directly in XAML the new class without additional code behind which is usefull especailly when you have "pure" MVVM code.

查看更多
祖国的老花朵
5楼-- · 2020-06-09 08:16

ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ActualHeight) should work, but if your code has just now updated the content of the scroll viewer, you may want to call ScrollViewer.UpdateLayout() before scrolling, so that ScrollViewer.ActualHeight will be up to date.

查看更多
Luminary・发光体
6楼-- · 2020-06-09 08:18

For WindowsPhone Universal app you can use:

 var scrollableHeight = ScrollViewer.ScrollableHeight;
        if (scrollableHeight > 0)
        {
           ScrollViewer.ScrollToVerticalOffset(scrollableHeight);
        }
查看更多
男人必须洒脱
7楼-- · 2020-06-09 08:20

no random asynchronicity is never the answer to anything in any language except maybe JavaScript... it just always has that "code smell" you know?

the proper way to do this is to force a "refresh" of the measure of the ScrollViewer synchronously to include the newly added children by calling ScrollViewer.Measure(Size)

after you "re-measure" the ScrollableHeight will be the proper value and you can use it as you normally would..

like this:

    private void AddMessage(string text, Color color)
    {
        var message = new TextBlock
        {
            Text = text,
            FontSize = 18,
            TextWrapping = TextWrapping.Wrap,
            Margin = new Thickness(10, 3, 10, 0),
            Foreground = new SolidColorBrush(color),
        };

        chatViewContent.Children.Add(message);

        chatViewScroller.Measure(chatViewScroller.RenderSize);
        chatViewScroller.ScrollToVerticalOffset(chatViewScroller.ScrollableHeight);
    }

the Size we are using here is the RenderSize which could technically be incorrect in some extreme edge cases, but it is pretty close to perfectly ideal for our purposes. you could also technically use an arbitrarily large Size for the Measure(Size) and get the same effect. whatever works best for your solution, hope this helps -ck

查看更多
登录 后发表回答