Skip to content

Commit 4590172

Browse files
committed
Changed SmoothScrollIntoView method to truly asynchronous
1 parent 8ecc496 commit 4590172

File tree

2 files changed

+42
-23
lines changed

2 files changed

+42
-23
lines changed

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public void OnXamlRendered(FrameworkElement control)
3737

3838
private void Load()
3939
{
40-
SampleController.Current.RegisterNewCommand("Start Smooth Scroll", (sender, args) =>
40+
SampleController.Current.RegisterNewCommand("Start Smooth Scroll", async (sender, args) =>
4141
{
4242
var index = int.TryParse(IndexInput.Text, out var i) ? i : 0;
4343
var itemPlacement = ItemPlacementInput.SelectedItem switch
@@ -55,7 +55,7 @@ private void Load()
5555
var scrollIfVisibile = ScrollIfVisibileInput.IsChecked ?? true;
5656
var additionalHorizontalOffset = int.TryParse(AdditionalHorizontalOffsetInput.Text, out var ho) ? ho : 0;
5757
var additionalVerticalOffset = int.TryParse(AdditionalVerticalOffsetInput.Text, out var vo) ? vo : 0;
58-
sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
58+
await sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
5959
});
6060

6161
if (sampleListView != null)

Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static partial class ListViewExtensions
2626
/// <param name="scrollIfVisible">Set false to disable scrolling when the corresponding item is in view</param>
2727
/// <param name="additionalHorizontalOffset">Adds additional horizontal offset</param>
2828
/// <param name="additionalVerticalOffset">Adds additional vertical offset</param>
29-
/// <returns>Note: Even though this return <see cref="Task"/>, it will not wait until the scrolling completes</returns>
29+
/// <returns>Returns <see cref="Task"/> that completes after scrolling</returns>
3030
public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase listViewBase, int index, ScrollItemPlacement itemPlacement = ScrollItemPlacement.Default, bool disableAnimation = false, bool scrollIfVisible = true, int additionalHorizontalOffset = 0, int additionalVerticalOffset = 0)
3131
{
3232
if (index > (listViewBase.Items.Count - 1))
@@ -56,15 +56,15 @@ public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase li
5656
previousXOffset = scrollViewer.HorizontalOffset;
5757
previousYOffset = scrollViewer.VerticalOffset;
5858

59-
var tcs = new TaskCompletionSource<object>();
59+
var tcs = new TaskCompletionSource<VoidResult>();
6060

61-
void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: null);
61+
void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: default);
6262

6363
try
6464
{
6565
scrollViewer.ViewChanged += ViewChanged;
6666
listViewBase.ScrollIntoView(listViewBase.Items[index], ScrollIntoViewAlignment.Leading);
67-
await tcs.Task;
67+
await tcs.Task.ConfigureAwait(true);
6868
}
6969
finally
7070
{
@@ -80,20 +80,7 @@ public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase li
8080
// Scrolling back to previous position
8181
if (isVirtualizing)
8282
{
83-
var tcs = new TaskCompletionSource<object>();
84-
85-
void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: null);
86-
87-
try
88-
{
89-
scrollViewer.ViewChanged += ViewChanged;
90-
scrollViewer.ChangeView(previousXOffset, previousYOffset, zoomFactor: null, disableAnimation: true);
91-
await tcs.Task;
92-
}
93-
finally
94-
{
95-
scrollViewer.ViewChanged -= ViewChanged;
96-
}
83+
await scrollViewer.ChangeViewAsync(previousXOffset, previousYOffset, zoomFactor: null, disableAnimation: true).ConfigureAwait(true);
9784
}
9885

9986
var listViewBaseWidth = listViewBase.ActualWidth;
@@ -185,7 +172,7 @@ public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase li
185172
}
186173
}
187174

188-
scrollViewer.ChangeView(finalXPosition, finalYPosition, zoomFactor: null, disableAnimation);
175+
await scrollViewer.ChangeViewAsync(finalXPosition, finalYPosition, zoomFactor: null, disableAnimation).ConfigureAwait(true);
189176
}
190177

191178
/// <summary>
@@ -198,10 +185,42 @@ public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase li
198185
/// <param name="scrollIfVisibile">Set true to disable scrolling when the corresponding item is in view</param>
199186
/// <param name="additionalHorizontalOffset">Adds additional horizontal offset</param>
200187
/// <param name="additionalVerticalOffset">Adds additional vertical offset</param>
201-
/// <returns>Note: Even though this return <see cref="Task"/>, it will not wait until the scrolling completes</returns>
188+
/// <returns>Returns <see cref="Task"/> that completes after scrolling</returns>
202189
public static async Task SmoothScrollIntoViewWithItemAsync(this ListViewBase listViewBase, object item, ScrollItemPlacement itemPlacement = ScrollItemPlacement.Default, bool disableAnimation = false, bool scrollIfVisibile = true, int additionalHorizontalOffset = 0, int additionalVerticalOffset = 0)
203190
{
204-
await SmoothScrollIntoViewWithIndexAsync(listViewBase, listViewBase.Items.IndexOf(item), itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
191+
await SmoothScrollIntoViewWithIndexAsync(listViewBase, listViewBase.Items.IndexOf(item), itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset).ConfigureAwait(true);
192+
}
193+
194+
/// <summary>
195+
/// Changes the view of <see cref="ScrollViewer"/> asynchronous.
196+
/// </summary>
197+
/// <param name="scrollViewer">The scroll viewer.</param>
198+
/// <param name="horizontalOffset">The horizontal offset.</param>
199+
/// <param name="verticalOffset">The vertical offset.</param>
200+
/// <param name="zoomFactor">The zoom factor.</param>
201+
/// <param name="disableAnimation">if set to <c>true</c> disable animation.</param>
202+
private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation)
203+
{
204+
var tcs = new TaskCompletionSource<VoidResult>();
205+
206+
void ViewChanged(object _, ScrollViewerViewChangedEventArgs __) => tcs.TrySetResult(result: default);
207+
208+
try
209+
{
210+
scrollViewer.ViewChanged += ViewChanged;
211+
scrollViewer.ChangeView(horizontalOffset, verticalOffset, zoomFactor, disableAnimation);
212+
await tcs.Task.ConfigureAwait(true);
213+
}
214+
finally
215+
{
216+
scrollViewer.ViewChanged -= ViewChanged;
217+
}
205218
}
219+
220+
/// <summary>
221+
/// Used as a placeholder TResult to indicate that a <![CDATA[Task<TResult>]]> has a void TResult
222+
/// </summary>
223+
/// <see href="https://referencesource.microsoft.com/#System.Core/System/Threading/Tasks/TaskExtensions.cs,6e36a68760fb02e6,references"/>
224+
private struct VoidResult { }
206225
}
207226
}

0 commit comments

Comments
 (0)