Scroll to new item in ListView for UWP
.net, c#, uwp, windows-community-toolkit, xaml
Solution
As of Windows 10, version 1607 you can use `ItemsStackPanel`.`ItemsUpdatingScrollMode` with the value `KeepLastItemInView`, which seems like the most natural fit for the job.
There is an "Inverted Lists" example in MS UWP docs (2017-2-8) that would boil down to this XAML:
<ListView Source="{Binding Messages}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel
VerticalAlignment="Bottom"
ItemsUpdatingScrollMode="KeepLastItemInView"
/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
On a side note, yes, I'd agree that you may want to get rid of a `ScrollViewer` as it's redundant as a `ListView` wrapper.
Upd:
`KeepLastItemInView` is not available for applications that target Windows 10 prior to the "Anniversary Edition". If that's the case, one way to make sure that a list always displays the last item after item collection is changed is to override `OnItemsChanged` and call `ScrollIntoView`. A basic implementation would look like this:
using System.Linq;
using Windows.UI.Xaml.Controls;
public class ChatListView : ListView
{
protected override void OnItemsChanged(object e)
{
base.OnItemsChanged(e);
if(Items.Count > 0) ScrollIntoView(Items.Last());
}
}
Problem
I'm creating a chat application with a ListView that contains the messages. When a new message is sent/received, the ListView should scroll to the new message. I'm using MVVM, so the ListView looks like ``` <ScrollViewer> <ItemsControl Source="{Binding Messages}" /> </ScrollViewer> ``` How can I do it? EDIT: I tried to make this work in versions prior to the Anniversary Update creating a Behavior. This is what I have so far: ``` public class FocusLastBehavior : Behavior<ItemsControl> { protected override void OnAttached() { base.OnAttached(); AssociatedObject.Items.VectorChanged += ItemsOnVectorChanged; } private void ItemsOnVectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs @event) { var scroll = VisualTreeExtensions.FindVisualAscendant<ScrollViewer>(AssociatedObject); if (scroll == null) { return; } var last = AssociatedObject.Items.LastOrDefault(); if (last == null) { return; } var container = AssociatedObject.ContainerFromItem(last); ScrollToElement(scroll, (UIElement)container); } private static void ScrollToElement(ScrollViewer scrollViewer, UIElement element, bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null) { var transform = element.TransformToVisual((UIElement)scrollViewer.Content); var position = transform.TransformPoint(new Point(0, 0)); if (isVerticalScrolling) { scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling); } else { scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling); } } } ``` The code uses VisualTreeExtensions from the UWP Community Toolkit However, the position after the call to TransformPoint always returns `{0, 0}` What am I doing wrong?