@@ -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 ) )
@@ -58,7 +58,7 @@ public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase li
5858
5959 var tcs = new TaskCompletionSource < object > ( ) ;
6060
61- void ViewChanged ( object obj , ScrollViewerViewChangedEventArgs args ) => tcs . TrySetResult ( result : null ) ;
61+ void ViewChanged ( object _ , ScrollViewerViewChangedEventArgs __ ) => tcs . TrySetResult ( result : default ) ;
6262
6363 try
6464 {
@@ -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 ) ;
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 ) ;
189176 }
190177
191178 /// <summary>
@@ -198,10 +185,68 @@ 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 {
204191 await SmoothScrollIntoViewWithIndexAsync ( listViewBase , listViewBase . Items . IndexOf ( item ) , itemPlacement , disableAnimation , scrollIfVisibile , additionalHorizontalOffset , additionalVerticalOffset ) ;
205192 }
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+ if ( horizontalOffset > scrollViewer . ScrollableWidth )
205+ {
206+ horizontalOffset = scrollViewer . ScrollableWidth ;
207+ }
208+ else if ( horizontalOffset < 0 )
209+ {
210+ horizontalOffset = 0 ;
211+ }
212+
213+ if ( verticalOffset > scrollViewer . ScrollableHeight )
214+ {
215+ verticalOffset = scrollViewer . ScrollableHeight ;
216+ }
217+ else if ( verticalOffset < 0 )
218+ {
219+ verticalOffset = 0 ;
220+ }
221+
222+ // MUST check this and return immediately, otherwise this async task will never complete because ViewChanged event won't get triggered
223+ if ( horizontalOffset == scrollViewer . HorizontalOffset && verticalOffset == scrollViewer . VerticalOffset )
224+ {
225+ return ;
226+ }
227+
228+ var tcs = new TaskCompletionSource < object > ( ) ;
229+
230+ void ViewChanged ( object _ , ScrollViewerViewChangedEventArgs e )
231+ {
232+ if ( e . IsIntermediate )
233+ {
234+ return ;
235+ }
236+
237+ tcs . TrySetResult ( result : default ) ;
238+ }
239+
240+ try
241+ {
242+ scrollViewer . ViewChanged += ViewChanged ;
243+ scrollViewer . ChangeView ( horizontalOffset , verticalOffset , zoomFactor , disableAnimation ) ;
244+ await tcs . Task ;
245+ }
246+ finally
247+ {
248+ scrollViewer . ViewChanged -= ViewChanged ;
249+ }
250+ }
206251 }
207252}
0 commit comments