diff --git a/WinUIGallery/Samples/ListView/ListViewPage.xaml b/WinUIGallery/Samples/ListView/ListViewPage.xaml
index 9d1fb2b4b..207a6a605 100644
--- a/WinUIGallery/Samples/ListView/ListViewPage.xaml
+++ b/WinUIGallery/Samples/ListView/ListViewPage.xaml
@@ -484,5 +484,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WinUIGallery/Samples/ListView/ListViewPage.xaml.cs b/WinUIGallery/Samples/ListView/ListViewPage.xaml.cs
index a63b69ba5..5adb0743e 100644
--- a/WinUIGallery/Samples/ListView/ListViewPage.xaml.cs
+++ b/WinUIGallery/Samples/ListView/ListViewPage.xaml.cs
@@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using WinUIGallery.Helpers;
@@ -22,6 +23,10 @@ public sealed partial class ListViewPage : ItemsPageBase
ObservableCollection contacts3 = new ObservableCollection();
ObservableCollection contacts3Filtered = new ObservableCollection();
ObservableCollection contacts4ContextMenu = new ObservableCollection();
+ ObservableCollection contacts5 = new ObservableCollection();
+ ObservableCollection contacts6 = new ObservableCollection();
+
+ private string _persistedPosition = string.Empty;
ItemsStackPanel? stackPanelObj;
@@ -67,6 +72,26 @@ protected override async void OnNavigatedTo(NavigationEventArgs e)
contacts4ContextMenu = await Contact.GetContactsAsync();
ContextMenuList.ItemsSource = contacts4ContextMenu;
+ // Populate ListViews for the RestoreScrollPosition and ScrollIntoView samples.
+ // Pre-format each entry with its index so the position in the list is easy to track.
+ var baseContacts = await Contact.GetContactsAsync();
+ contacts5 = new ObservableCollection(
+ baseContacts.Select((c, i) => $"{i + 1}. {c.Name}"));
+
+ // ScrollIntoView is more interesting with a long list, so seed multiple copies.
+ var manyContacts = new List();
+ int n = 1;
+ for (int i = 0; i < 10; i++)
+ {
+ foreach (var c in baseContacts)
+ {
+ manyContacts.Add($"{n++}. {c.Name}");
+ }
+ }
+ contacts6 = new ObservableCollection(manyContacts);
+ RestoreScrollListView.ItemsSource = contacts5;
+ ScrollIntoViewListView.ItemsSource = contacts6;
+
FilteredListView.ItemsSource = contacts3Filtered;
}
@@ -319,6 +344,56 @@ private void ContactDeleteMenuItem_Click(object sender, RoutedEventArgs e)
contacts4ContextMenu.Remove(contact);
}
+
+ //===================================================================================================================
+ // Save / restore scroll position sample
+ //===================================================================================================================
+
+ private void SavePositionButton_Click(object sender, RoutedEventArgs e)
+ {
+ // GetRelativeScrollPosition returns an opaque string identifying the item at the
+ // top of the viewport plus its pixel offset. Store it for later restoration.
+ _persistedPosition = ListViewPersistenceHelper.GetRelativeScrollPosition(
+ RestoreScrollListView,
+ item => item as string ?? string.Empty);
+
+ SavedPositionTextBlock.Text = $"Saved position: {_persistedPosition}";
+ RestorePositionButton.IsEnabled = true;
+ }
+
+ private async void RestorePositionButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(_persistedPosition))
+ {
+ return;
+ }
+
+ // SetRelativeScrollPositionAsync scrolls the ListView back to the item identified
+ // by the saved string. The callback resolves a key back to its data item.
+ await ListViewPersistenceHelper.SetRelativeScrollPositionAsync(
+ RestoreScrollListView,
+ _persistedPosition,
+ key => Task.FromResult