浏览代码

Improve pen secondary button handling on list box (#18766)

* Only start ScrollGesture when left click pressed, also `GetCurrentPoint(null)` behaves the same as root visual

* Allow right-click pen to select items on press

* Add context menus to even items on ListBox page for testing

* Avoid global static in UpdateSelectionFromPointerEvent

* Revert "Avoid global static in UpdateSelectionFromPointerEvent"

This reverts commit 2562d73e83b238ae0eda1c2c54247b223f539034.

* Add comment to UpdateSelectionFromPointerEvent

* Use fully mocked rendering for list box test

* Add pen selection tests

* TouchTestHelper should use correct inputs
Maxwell Katz 5 月之前
父节点
当前提交
338a5ea0a2

+ 7 - 0
samples/ControlCatalog/Pages/ListBoxPage.xaml

@@ -5,6 +5,13 @@
              x:DataType="viewModels:ListBoxPageViewModel">
              x:DataType="viewModels:ListBoxPageViewModel">
   <DockPanel>
   <DockPanel>
     <DockPanel.Styles>
     <DockPanel.Styles>
+      <Style Selector="ListBox ListBoxItem:nth-child(even)">
+        <Setter Property="ContextFlyout">
+          <MenuFlyout>
+            <MenuItem Header="Hello there" />
+          </MenuFlyout>
+        </Setter>
+      </Style>
       <Style Selector="ListBox ListBoxItem:nth-child(5n+3)">
       <Style Selector="ListBox ListBoxItem:nth-child(5n+3)">
         <Setter Property="Foreground" Value="Red" />
         <Setter Property="Foreground" Value="Red" />
         <Setter Property="FontWeight" Value="Bold" />
         <Setter Property="FontWeight" Value="Bold" />

+ 6 - 6
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@@ -23,7 +23,6 @@ namespace Avalonia.Input.GestureRecognizers
         private int _gestureId;
         private int _gestureId;
         private Point _pointerPressedPoint;
         private Point _pointerPressedPoint;
         private VelocityTracker? _velocityTracker;
         private VelocityTracker? _velocityTracker;
-        private Visual? _rootTarget;
 
 
         // Movement per second
         // Movement per second
         private Vector _inertia;
         private Vector _inertia;
@@ -96,13 +95,15 @@ namespace Avalonia.Input.GestureRecognizers
 
 
         protected override void PointerPressed(PointerPressedEventArgs e)
         protected override void PointerPressed(PointerPressedEventArgs e)
         {
         {
-            if (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)
+            var point = e.GetCurrentPoint(null);
+
+            if (e.Pointer.Type is PointerType.Touch or PointerType.Pen
+                && point.Properties.IsLeftButtonPressed)
             {
             {
                 EndGesture();
                 EndGesture();
                 _tracking = e.Pointer;
                 _tracking = e.Pointer;
                 _gestureId = ScrollGestureEventArgs.GetNextFreeId();
                 _gestureId = ScrollGestureEventArgs.GetNextFreeId();
-                _rootTarget = (Visual?)(Target as Visual)?.VisualRoot;
-                _trackedRootPoint = _pointerPressedPoint = e.GetPosition(_rootTarget);
+                _trackedRootPoint = _pointerPressedPoint = point.Position;
                 _velocityTracker = new VelocityTracker();
                 _velocityTracker = new VelocityTracker();
                 _velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), default);
                 _velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), default);
             }
             }
@@ -112,7 +113,7 @@ namespace Avalonia.Input.GestureRecognizers
         {
         {
             if (e.Pointer == _tracking)
             if (e.Pointer == _tracking)
             {
             {
-                var rootPoint = e.GetPosition(_rootTarget);
+                var rootPoint = e.GetPosition(null);
                 if (!_scrolling)
                 if (!_scrolling)
                 {
                 {
                     if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)
                     if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)
@@ -159,7 +160,6 @@ namespace Avalonia.Input.GestureRecognizers
                 Target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId));
                 Target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId));
                 _gestureId = 0;
                 _gestureId = 0;
                 _lastMoveTimestamp = null;
                 _lastMoveTimestamp = null;
-                _rootTarget = null;
             }
             }
             
             
         }
         }

+ 1 - 0
src/Avalonia.Controls/ListBox.cs

@@ -166,6 +166,7 @@ namespace Avalonia.Controls
 
 
         internal bool UpdateSelectionFromPointerEvent(Control source, PointerEventArgs e)
         internal bool UpdateSelectionFromPointerEvent(Control source, PointerEventArgs e)
         {
         {
+            // TODO: use TopLevel.PlatformSettings here, but first need to update our tests to use TopLevels. 
             var hotkeys = Application.Current!.PlatformSettings?.HotkeyConfiguration;
             var hotkeys = Application.Current!.PlatformSettings?.HotkeyConfiguration;
             var toggle = hotkeys is not null && e.KeyModifiers.HasAllFlags(hotkeys.CommandModifiers);
             var toggle = hotkeys is not null && e.KeyModifiers.HasAllFlags(hotkeys.CommandModifiers);
 
 

+ 4 - 2
src/Avalonia.Controls/ListBoxItem.cs

@@ -64,9 +64,11 @@ namespace Avalonia.Controls
                 if (p.Properties.PointerUpdateKind is PointerUpdateKind.LeftButtonPressed or 
                 if (p.Properties.PointerUpdateKind is PointerUpdateKind.LeftButtonPressed or 
                     PointerUpdateKind.RightButtonPressed)
                     PointerUpdateKind.RightButtonPressed)
                 {
                 {
-                    if (p.Pointer.Type == PointerType.Mouse)
+                    if (p.Pointer.Type == PointerType.Mouse
+                        || (p.Pointer.Type == PointerType.Pen && p.Properties.IsRightButtonPressed))
                     {
                     {
-                        // If the pressed point comes from a mouse, perform the selection immediately.
+                        // If the pressed point comes from a mouse or right-click pen, perform the selection immediately.
+                        // In case of pen, only right-click is accepted, as left click (a tip touch) is used for scrolling. 
                         e.Handled = owner.UpdateSelectionFromPointerEvent(this, e);
                         e.Handled = owner.UpdateSelectionFromPointerEvent(this, e);
                     }
                     }
                     else
                     else

+ 1 - 27
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@@ -611,17 +611,6 @@ namespace Avalonia.Controls.UnitTests
                 }.RegisterInNameScope(scope));
                 }.RegisterInNameScope(scope));
         }
         }
 
 
-        private static FuncControlTemplate ListBoxItemTemplate()
-        {
-            return new FuncControlTemplate<ListBoxItem>((parent, scope) =>
-                new ContentPresenter
-                {
-                    Name = "PART_ContentPresenter",
-                    [!ContentPresenter.ContentProperty] = parent[!ListBoxItem.ContentProperty],
-                    [!ContentPresenter.ContentTemplateProperty] = parent[!ListBoxItem.ContentTemplateProperty],
-                }.RegisterInNameScope(scope));
-        }
-
         private static FuncControlTemplate ScrollViewerTemplate()
         private static FuncControlTemplate ScrollViewerTemplate()
         {
         {
             return new FuncControlTemplate<ScrollViewer>((parent, scope) =>
             return new FuncControlTemplate<ScrollViewer>((parent, scope) =>
@@ -645,21 +634,7 @@ namespace Avalonia.Controls.UnitTests
         private static void Prepare(ListBox target)
         private static void Prepare(ListBox target)
         {
         {
             target.Width = target.Height = 100;
             target.Width = target.Height = 100;
-
-            var root = new TestRoot(target)
-            {
-                Resources =
-                {
-                    { 
-                        typeof(ListBoxItem),
-                        new ControlTheme(typeof(ListBoxItem))
-                        {
-                            Setters = { new Setter(ListBoxItem.TemplateProperty, ListBoxItemTemplate()) }
-                        }
-                    }
-                }
-            };
-
+            var root = new TestRoot(target);
             root.LayoutManager.ExecuteInitialLayoutPass();
             root.LayoutManager.ExecuteInitialLayoutPass();
         }
         }
 
 
@@ -1263,7 +1238,6 @@ namespace Avalonia.Controls.UnitTests
                     {
                     {
                         new ListBoxItem()
                         new ListBoxItem()
                         {
                         {
-                            Template = ListBoxItemTemplate(),
                             Content = target,
                             Content = target,
                         }
                         }
                     }
                     }

+ 131 - 61
tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs

@@ -6,6 +6,7 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
 using Avalonia.Data;
 using Avalonia.Input;
 using Avalonia.Input;
+using Avalonia.Input.GestureRecognizers;
 using Avalonia.Input.Platform;
 using Avalonia.Input.Platform;
 using Avalonia.LogicalTree;
 using Avalonia.LogicalTree;
 using Avalonia.Styling;
 using Avalonia.Styling;
@@ -19,30 +20,34 @@ namespace Avalonia.Controls.UnitTests
     public class ListBoxTests_Single : ScopedTestBase
     public class ListBoxTests_Single : ScopedTestBase
     {
     {
         MouseTestHelper _mouse = new MouseTestHelper();
         MouseTestHelper _mouse = new MouseTestHelper();
+        MouseTestHelper _pen = new MouseTestHelper(PointerType.Pen);
         
         
         [Fact]
         [Fact]
         public void Focusing_Item_With_Tab_Should_Not_Select_It()
         public void Focusing_Item_With_Tab_Should_Not_Select_It()
         {
         {
-            var target = new ListBox
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
-                Template = new FuncControlTemplate(CreateListBoxTemplate),
-                ItemsSource = new[] { "Foo", "Bar", "Baz " },
-            };
+                var target = new ListBox
+                {
+                    Template = new FuncControlTemplate(CreateListBoxTemplate),
+                    ItemsSource = new[] { "Foo", "Bar", "Baz " },
+                };
 
 
-            ApplyTemplate(target);
+                Prepare(target);
 
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
-            {
-                NavigationMethod = NavigationMethod.Tab,
-            });
+                target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
+                {
+                    NavigationMethod = NavigationMethod.Tab,
+                });
 
 
-            Assert.Equal(-1, target.SelectedIndex);
+                Assert.Equal(-1, target.SelectedIndex);
+            }
         }
         }
 
 
         [Fact]
         [Fact]
         public void Pressing_Space_On_Focused_Item_With_Ctrl_Pressed_Should_Select_It()
         public void Pressing_Space_On_Focused_Item_With_Ctrl_Pressed_Should_Select_It()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -50,7 +55,7 @@ namespace Avalonia.Controls.UnitTests
                     ItemsSource = new[] { "Foo", "Bar", "Baz " },
                     ItemsSource = new[] { "Foo", "Bar", "Baz " },
                 };
                 };
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
 
 
                 target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
                 target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
                 {
                 {
@@ -72,7 +77,7 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Clicking_Item_Should_Select_It()
         public void Clicking_Item_Should_Select_It()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -80,17 +85,88 @@ namespace Avalonia.Controls.UnitTests
                     ItemsSource = new[] { "Foo", "Bar", "Baz " },
                     ItemsSource = new[] { "Foo", "Bar", "Baz " },
                 };
                 };
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
 
 
                 Assert.Equal(0, target.SelectedIndex);
                 Assert.Equal(0, target.SelectedIndex);
             }
             }
         }
         }
 
 
+        [Fact]
+        public void Pen_Right_Press_Item_Should_Select_It()
+        {
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+            {
+                var target = new ListBox
+                {
+                    Template = new FuncControlTemplate(CreateListBoxTemplate),
+                    ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
+                    ItemsSource = new[] { "Foo", "Bar", "Baz " }
+                };
+                Prepare(target);
+                _pen.Down(target.Presenter.Panel.Children[0], MouseButton.Right);
+
+                Assert.Equal(0, target.SelectedIndex);
+            }
+        }
+
+        [Fact]
+        public void Pen_Left_Press_Item_Should_Not_Select_It()
+        {
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+            {
+                var target = new ListBox
+                {
+                    Template = new FuncControlTemplate(CreateListBoxTemplate),
+                    ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
+                    ItemsSource = new[] { "Foo", "Bar", "Baz " }
+                };
+                Prepare(target);
+                _pen.Down(target.Presenter.Panel.Children[0]);
+
+                Assert.Equal(-1, target.SelectedIndex);
+            }
+        }
+
+
+        [Theory]
+        [InlineData(PointerType.Mouse)]
+        [InlineData(PointerType.Pen)]
+        public void Pointer_Right_Click_Should_Select_Item_And_Open_Context(PointerType type)
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var target = new ListBox
+                {
+                    Template = new FuncControlTemplate(CreateListBoxTemplate),
+                    ItemsSource = new[] { "Foo", "Bar", "Baz " },
+                    ItemTemplate = new FuncDataTemplate<string>((x, _) => new Border { Height = 10 })
+                };
+                target.GestureRecognizers.Add(new ScrollGestureRecognizer()
+                {
+                    CanVerticallyScroll = true, ScrollStartDistance = 50
+                });
+                Prepare(target);
+
+                var contextRaised = false;
+                target.AddHandler(Control.ContextRequestedEvent, (sender, args) =>
+                {
+                    contextRaised = true;
+                    args.Handled = true;
+                });
+
+                var pointer = type == PointerType.Mouse ? _mouse : _pen;
+                pointer.Click(target.Presenter.Panel.Children[0], MouseButton.Right, position: new Point(5, 5));
+
+                Assert.True(contextRaised);
+                Assert.Equal(0, target.SelectedIndex);
+            }
+        }
+
         [Fact]
         [Fact]
         public void Clicking_Selected_Item_Should_Not_Deselect_It()
         public void Clicking_Selected_Item_Should_Not_Deselect_It()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -98,7 +174,7 @@ namespace Avalonia.Controls.UnitTests
                     ItemsSource = new[] { "Foo", "Bar", "Baz " },
                     ItemsSource = new[] { "Foo", "Bar", "Baz " },
                 };
                 };
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
                 target.SelectedIndex = 0;
                 target.SelectedIndex = 0;
 
 
                 _mouse.Click(target.Presenter.Panel.Children[0]);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
@@ -110,7 +186,7 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle()
         public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -119,7 +195,7 @@ namespace Avalonia.Controls.UnitTests
                     SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
                     SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
                 };
                 };
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
 
 
                 _mouse.Click(target.Presenter.Panel.Children[0]);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
 
 
@@ -130,7 +206,7 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle()
         public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -140,7 +216,7 @@ namespace Avalonia.Controls.UnitTests
                 };
                 };
 
 
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
                 target.SelectedIndex = 0;
                 target.SelectedIndex = 0;
 
 
                 _mouse.Click(target.Presenter.Panel.Children[0]);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
@@ -152,7 +228,7 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected()
         public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -161,7 +237,7 @@ namespace Avalonia.Controls.UnitTests
                     SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
                     SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
                 };
                 };
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
                 target.SelectedIndex = 0;
                 target.SelectedIndex = 0;
 
 
                 _mouse.Click(target.Presenter.Panel.Children[0]);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
@@ -173,7 +249,7 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle()
         public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle()
         {
         {
-            using (UnitTestApplication.Start())
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
                 var target = new ListBox
                 var target = new ListBox
                 {
                 {
@@ -182,7 +258,7 @@ namespace Avalonia.Controls.UnitTests
                     SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
                     SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
                 };
                 };
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
                 AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
-                ApplyTemplate(target);
+                Prepare(target);
                 target.SelectedIndex = 1;
                 target.SelectedIndex = 1;
 
 
                 _mouse.Click(target.Presenter.Panel.Children[0]);
                 _mouse.Click(target.Presenter.Panel.Children[0]);
@@ -194,45 +270,48 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Setting_Item_IsSelected_Sets_ListBox_Selection()
         public void Setting_Item_IsSelected_Sets_ListBox_Selection()
         {
         {
-            var target = new ListBox
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
-                Template = new FuncControlTemplate(CreateListBoxTemplate),
-                ItemsSource = new[] { "Foo", "Bar", "Baz " },
-            };
+                var target = new ListBox
+                {
+                    Template = new FuncControlTemplate(CreateListBoxTemplate),
+                    ItemsSource = new[] { "Foo", "Bar", "Baz " },
+                };
 
 
-            ApplyTemplate(target);
+                Prepare(target);
 
 
-            ((ListBoxItem)target.GetLogicalChildren().ElementAt(1)).IsSelected = true;
+                ((ListBoxItem)target.GetLogicalChildren().ElementAt(1)).IsSelected = true;
 
 
-            Assert.Equal("Bar", target.SelectedItem);
-            Assert.Equal(1, target.SelectedIndex);
+                Assert.Equal("Bar", target.SelectedItem);
+                Assert.Equal(1, target.SelectedIndex);
+            }
         }
         }
 
 
         [Fact]
         [Fact]
         public void SelectedItem_Should_Not_Cause_StackOverflow()
         public void SelectedItem_Should_Not_Cause_StackOverflow()
         {
         {
-            var viewModel = new TestStackOverflowViewModel()
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
             {
             {
-                Items = new List<string> { "foo", "bar", "baz" }
-            };
+                var viewModel = new TestStackOverflowViewModel() { Items = new List<string> { "foo", "bar", "baz" } };
 
 
-            var target = new ListBox
-            {
-                Template = new FuncControlTemplate(CreateListBoxTemplate),
-                DataContext = viewModel,
-                ItemsSource = viewModel.Items
-            };
+                var target = new ListBox
+                {
+                    Template = new FuncControlTemplate(CreateListBoxTemplate),
+                    DataContext = viewModel,
+                    ItemsSource = viewModel.Items
+                };
 
 
-            target.Bind(ListBox.SelectedItemProperty,
-                new Binding("SelectedItem") { Mode = BindingMode.TwoWay });
+                target.Bind(ListBox.SelectedItemProperty,
+                    new Binding("SelectedItem") { Mode = BindingMode.TwoWay });
 
 
-            Assert.Equal(0, viewModel.SetterInvokedCount);
+                Assert.Equal(0, viewModel.SetterInvokedCount);
 
 
-            // In Issue #855, a Stackoverflow occurred here.
-            target.SelectedItem = viewModel.Items[2];
+                // In Issue #855, a Stackoverflow occurred here.
+                target.SelectedItem = viewModel.Items[2];
 
 
-            Assert.Equal(viewModel.Items[1], target.SelectedItem);
-            Assert.Equal(1, viewModel.SetterInvokedCount);
+                Assert.Equal(viewModel.Items[1], target.SelectedItem);
+                Assert.Equal(1, viewModel.SetterInvokedCount);
+            }
         }
         }
 
 
         private class TestStackOverflowViewModel : INotifyPropertyChanged
         private class TestStackOverflowViewModel : INotifyPropertyChanged
@@ -295,20 +374,11 @@ namespace Avalonia.Controls.UnitTests
             }.RegisterInNameScope(scope);
             }.RegisterInNameScope(scope);
         }
         }
 
 
-        private static void ApplyTemplate(ListBox target)
+        private static void Prepare(ListBox target)
         {
         {
-            // Apply the template to the ListBox itself.
-            target.ApplyTemplate();
-
-            // Then to its inner ScrollViewer.
-            var scrollViewer = (ScrollViewer)target.GetVisualChildren().Single();
-            scrollViewer.ApplyTemplate();
-
-            // Then make the ScrollViewer create its child.
-            scrollViewer.Presenter.UpdateChild();
-
-            // Now the ItemsPresenter should be registered, so apply its template.
-            target.Presenter.ApplyTemplate();
+            target.Width = target.Height = 100;
+            var root = new TestRoot(target);
+            root.LayoutManager.ExecuteInitialLayoutPass();
         }
         }
     }
     }
 }
 }

+ 2 - 2
tests/Avalonia.UnitTests/MouseTestHelper.cs

@@ -4,9 +4,9 @@ using Avalonia.VisualTree;
 
 
 namespace Avalonia.UnitTests
 namespace Avalonia.UnitTests
 {
 {
-    public class MouseTestHelper
+    public class MouseTestHelper(PointerType pointerType = PointerType.Mouse)
     {
     {
-        private readonly Pointer _pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);
+        private readonly Pointer _pointer = new Pointer(Pointer.GetNextFreeId(), pointerType, true);
         private ulong _nextStamp = 1;
         private ulong _nextStamp = 1;
         private ulong Timestamp() => _nextStamp++;
         private ulong Timestamp() => _nextStamp++;
 
 

+ 5 - 4
tests/Avalonia.UnitTests/TouchTestHelper.cs

@@ -19,7 +19,8 @@ namespace Avalonia.UnitTests
         public void Down(Interactive target, Interactive source, Point position = default, KeyModifiers modifiers = default)
         public void Down(Interactive target, Interactive source, Point position = default, KeyModifiers modifiers = default)
         {
         {
             _pointer.Capture((IInputElement)target);
             _pointer.Capture((IInputElement)target);
-            source.RaiseEvent(new PointerPressedEventArgs(source, _pointer, (Visual)source, position, Timestamp(), PointerPointProperties.None,
+            source.RaiseEvent(new PointerPressedEventArgs(source, _pointer, (Visual)source, position, Timestamp(),
+                new(RawInputModifiers.LeftMouseButton, PointerUpdateKind.LeftButtonPressed),
                 modifiers));
                 modifiers));
         }
         }
 
 
@@ -28,7 +29,7 @@ namespace Avalonia.UnitTests
         public void Move(Interactive target, Interactive source, in Point position, KeyModifiers modifiers = default)
         public void Move(Interactive target, Interactive source, in Point position, KeyModifiers modifiers = default)
         {
         {
             var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position,
             var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position,
-                Timestamp(), PointerPointProperties.None, modifiers);
+                Timestamp(), new(RawInputModifiers.LeftMouseButton, PointerUpdateKind.Other), modifiers);
             if (_pointer.CapturedGestureRecognizer != null)
             if (_pointer.CapturedGestureRecognizer != null)
                 _pointer.CapturedGestureRecognizer.PointerMovedInternal(e);
                 _pointer.CapturedGestureRecognizer.PointerMovedInternal(e);
             else
             else
@@ -41,8 +42,8 @@ namespace Avalonia.UnitTests
 
 
         public void Up(Interactive target, Interactive source, Point position = default, KeyModifiers modifiers = default)
         public void Up(Interactive target, Interactive source, Point position = default, KeyModifiers modifiers = default)
         {
         {
-            var e = new PointerReleasedEventArgs(source, _pointer, (Visual)target, position, Timestamp(), PointerPointProperties.None,
-                modifiers, MouseButton.None);
+            var e = new PointerReleasedEventArgs(source, _pointer, (Visual)target, position, Timestamp(),
+                new(RawInputModifiers.None, PointerUpdateKind.LeftButtonReleased), modifiers, MouseButton.Left);
 
 
             if (_pointer.CapturedGestureRecognizer != null)
             if (_pointer.CapturedGestureRecognizer != null)
                 _pointer.CapturedGestureRecognizer.PointerReleasedInternal(e);
                 _pointer.CapturedGestureRecognizer.PointerReleasedInternal(e);