Skip to content

Commit 51cbc9a

Browse files
Merge pull request #4129 from Vijay-Nirmal/SmoothScrollMadeAsync
Changed SmoothScrollIntoView method to be truly asynchronous
2 parents e4e06cb + b6749fc commit 51cbc9a

File tree

3 files changed

+100
-28
lines changed

3 files changed

+100
-28
lines changed

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@
3535
</Grid>
3636

3737
<StackPanel Grid.Column="1" Margin="5,10,10,0" Width="200">
38-
<TextBlock Text="Smooth Scroll Settings" FontSize="{StaticResource TextStyleLargeFontSize}" Margin="0,0,0,10"/>
38+
<TextBlock Text="Smooth Scroll Settings" FontSize="{StaticResource TextStyleLargeFontSize}"/>
3939
<TextBox x:Name="IndexInput"
4040
Header="Index"
4141
InputScope="Number"
42-
Text="100" />
42+
Text="100"
43+
Margin="0,10,0,0" />
4344
<ComboBox x:Name="ItemPlacementInput"
4445
Header="Item Placement"
45-
SelectedIndex="0">
46+
SelectedIndex="0"
47+
Margin="0,10,0,0" >
4648
<x:String>Default</x:String>
4749
<x:String>Left</x:String>
4850
<x:String>Top</x:String>
@@ -52,18 +54,25 @@
5254
</ComboBox>
5355
<CheckBox x:Name="DisableAnimationInput"
5456
Content="Disable Animation"
55-
IsChecked="False" />
57+
IsChecked="False"
58+
Margin="0,10,0,0" />
5659
<CheckBox x:Name="ScrollIfVisibileInput"
5760
Content="Scroll If Visible"
5861
IsChecked="True" />
5962
<TextBox x:Name="AdditionalHorizontalOffsetInput"
6063
Header="Horizontal Offset"
6164
InputScope="Number"
62-
Text="0" />
65+
Text="0"
66+
Margin="0,10,0,0" />
6367
<TextBox x:Name="AdditionalVerticalOffsetInput"
6468
Header="Vertical Offset"
6569
InputScope="Number"
66-
Text="0" />
70+
Text="0"
71+
Margin="0,10,0,0" />
72+
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
73+
<Ellipse x:Name="ScrollIndicator" Fill="Red" Width="15" Height="15"/>
74+
<TextBlock x:Name="ScrollIndicatorTest" Text="Not Scrolling" Margin="10,0,0,0"/>
75+
</StackPanel>
6776
</StackPanel>
6877
</Grid>
69-
</Page>
78+
</Page>

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
using System.Collections.ObjectModel;
77
using System.Windows.Input;
88
using Microsoft.Toolkit.Uwp.UI;
9+
using Windows.UI;
910
using Windows.UI.Popups;
1011
using Windows.UI.Xaml;
1112
using Windows.UI.Xaml.Controls;
13+
using Windows.UI.Xaml.Media;
1214

1315
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
1416
{
@@ -37,7 +39,7 @@ public void OnXamlRendered(FrameworkElement control)
3739

3840
private void Load()
3941
{
40-
SampleController.Current.RegisterNewCommand("Start Smooth Scroll", (sender, args) =>
42+
SampleController.Current.RegisterNewCommand("Start Smooth Scroll", async (sender, args) =>
4143
{
4244
var index = int.TryParse(IndexInput.Text, out var i) ? i : 0;
4345
var itemPlacement = ItemPlacementInput.SelectedItem switch
@@ -55,7 +57,9 @@ private void Load()
5557
var scrollIfVisibile = ScrollIfVisibileInput.IsChecked ?? true;
5658
var additionalHorizontalOffset = int.TryParse(AdditionalHorizontalOffsetInput.Text, out var ho) ? ho : 0;
5759
var additionalVerticalOffset = int.TryParse(AdditionalVerticalOffsetInput.Text, out var vo) ? vo : 0;
58-
sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
60+
UpdateScrollIndicator(true);
61+
await sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
62+
UpdateScrollIndicator(false);
5963
});
6064

6165
if (sampleListView != null)
@@ -64,6 +68,20 @@ private void Load()
6468
}
6569
}
6670

71+
private void UpdateScrollIndicator(bool isScrolling)
72+
{
73+
if (isScrolling)
74+
{
75+
ScrollIndicatorTest.Text = "Scrolling";
76+
ScrollIndicator.Fill = new SolidColorBrush(Colors.Green);
77+
}
78+
else
79+
{
80+
ScrollIndicator.Fill = new SolidColorBrush(Colors.Red);
81+
ScrollIndicatorTest.Text = "Not Scolling";
82+
}
83+
}
84+
6785
private ObservableCollection<string> GetOddEvenSource(int count)
6886
{
6987
var oddEvenSource = new ObservableCollection<string>();
@@ -103,4 +121,4 @@ private static async void OnExecuteSampleCommand(string item)
103121
await new MessageDialog($"You clicked {item} via the 'ListViewExtensions.Command' binding", "Item Clicked").ShowAsync();
104122
}
105123
}
106-
}
124+
}

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

Lines changed: 63 additions & 18 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))
@@ -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

Comments
 (0)