diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ScrollViewer.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ScrollViewer.cs index 1246a0e13ffb..7526fe32838c 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ScrollViewer.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_ScrollViewer.cs @@ -1511,6 +1511,43 @@ public async Task When_SCP_Content_EmptySized_WithMargin_LayoutCycle() await UITestHelper.Load(SUT, x => x.IsLoaded); } + [TestMethod] +#if !HAS_INPUT_INJECTOR || !UNO_HAS_MANAGED_SCROLL_PRESENTER + [Ignore("This test only applies to managed scroll presenter and requires input injector.")] +#endif + public async Task When_ScrollViewer_Touch_Scrolled() + { + var stackPanel = new StackPanel(); + var random = Random.Shared; + for (int i = 0; i < 10; i++) + { + stackPanel.Children.Add( + new Rectangle() + { + Width = 50, + Height = 50, + Fill = new SolidColorBrush(Color.FromArgb(255, (byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(256))) + }); + } + + var SUT = new ScrollViewer + { + Height = 300, + Content = stackPanel + }; + + await UITestHelper.Load(SUT); + + var input = InputInjector.TryCreate() ?? throw new InvalidOperationException("Pointer injection not available on this platform."); + using var finger = input.GetFinger(); + var bounds = SUT.GetAbsoluteBounds(); + finger.Press(bounds.GetCenter()); + finger.MoveTo(bounds.GetCenter().Offset(0, -50)); + finger.Release(); + await WindowHelper.WaitForIdle(); + Assert.AreEqual(50, SUT.VerticalOffset); + } + [TestMethod] public async Task When_Zero_Size_With_Margin() { diff --git a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs index 0c585caf63bc..bce88ecdcca5 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/ScrollContentPresenter.Managed.cs @@ -4,6 +4,8 @@ using Windows.Devices.Input; using Windows.Foundation; using Uno.UI.Xaml; +using Uno.Disposables; + #if HAS_UNO_WINUI using _PointerDeviceType = global::Microsoft.UI.Input.PointerDeviceType; @@ -56,6 +58,8 @@ public bool CanVerticallyScroll private object RealContent => Content; + private readonly SerialDisposable _eventSubscriptions = new(); + partial void InitializePartial() { #if __SKIA__ @@ -71,6 +75,8 @@ partial void InitializePartial() private void HookScrollEvents(ScrollViewer sv) { + UnhookScrollEvents(sv); + // Note: the way WinUI does scrolling is very different, and doesn't use // PointerWheelChanged changes, etc. // We can either subscribe on the ScrollViewer or the SCP directly, but due to @@ -87,16 +93,21 @@ private void HookScrollEvents(ScrollViewer sv) ManipulationStarted += TouchScrollStarted; ManipulationDelta += UpdateTouchScroll; ManipulationCompleted += CompleteTouchScroll; + + _eventSubscriptions.Disposable = Disposable.Create(() => + { + sv.PointerWheelChanged -= PointerWheelScroll; + + ManipulationStarting -= PrepareTouchScroll; + ManipulationStarted -= TouchScrollStarted; + ManipulationDelta -= UpdateTouchScroll; + ManipulationCompleted -= CompleteTouchScroll; + }); } private void UnhookScrollEvents(ScrollViewer sv) { - sv.PointerWheelChanged -= PointerWheelScroll; - - ManipulationStarting -= PrepareTouchScroll; - ManipulationStarted -= TouchScrollStarted; - ManipulationDelta -= UpdateTouchScroll; - ManipulationCompleted -= CompleteTouchScroll; + _eventSubscriptions.Disposable = null; } private protected override void OnLoaded()