ソースを参照

Don't raise SelectionModel.PropertyChanged during update.

Steven Kirk 5 年 前
コミット
7f87c2e74a

+ 22 - 13
src/Avalonia.Controls/SelectionModel.cs

@@ -20,6 +20,7 @@ namespace Avalonia.Controls
         private bool _singleSelect;
         private bool _autoSelect;
         private int _operationCount;
+        private IndexPath _oldAnchorIndex;
         private IReadOnlyList<IndexPath>? _selectedIndicesCached;
         private IReadOnlyList<object?>? _selectedItemsCached;
         private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs;
@@ -142,6 +143,8 @@ namespace Avalonia.Controls
             }
             set
             {
+                var oldValue = AnchorIndex;
+
                 if (value != null)
                 {
                     SelectionTreeHelper.TraverseIndexPath(
@@ -155,7 +158,10 @@ namespace Avalonia.Controls
                     _rootNode.AnchorIndex = -1;
                 }
 
-                RaisePropertyChanged("AnchorIndex");
+                if (_operationCount == 0 && oldValue != AnchorIndex)
+                {
+                    RaisePropertyChanged("AnchorIndex");
+                }
             }
         }
 
@@ -633,19 +639,18 @@ namespace Avalonia.Controls
             _selectedIndicesCached = null;
             _selectedItemsCached = null;
 
-            // Raise SelectionChanged event
             if (e != null)
             {
                 SelectionChanged?.Invoke(this, e);
-            }
 
-            RaisePropertyChanged(nameof(SelectedIndex));
-            RaisePropertyChanged(nameof(SelectedIndices));
+                RaisePropertyChanged(nameof(SelectedIndex));
+                RaisePropertyChanged(nameof(SelectedIndices));
 
-            if (_rootNode.Source != null)
-            {
-                RaisePropertyChanged(nameof(SelectedItem));
-                RaisePropertyChanged(nameof(SelectedItems));
+                if (_rootNode.Source != null)
+                {
+                    RaisePropertyChanged(nameof(SelectedItem));
+                    RaisePropertyChanged(nameof(SelectedItems));
+                }
             }
         }
 
@@ -785,6 +790,7 @@ namespace Avalonia.Controls
         {
             if (_operationCount++ == 0)
             {
+                _oldAnchorIndex = AnchorIndex;
                 _rootNode.BeginOperation();
             }
         }
@@ -808,13 +814,16 @@ namespace Avalonia.Controls
                     var changeSet = new SelectionModelChangeSet(changes);
                     e = changeSet.CreateEventArgs();
                 }
-            }
 
-            OnSelectionChanged(e);
+                OnSelectionChanged(e);
+                
+                if (_oldAnchorIndex != AnchorIndex)
+                {
+                    RaisePropertyChanged(nameof(AnchorIndex));
+                }
 
-            if (_operationCount == 0)
-            {
                 _rootNode.Cleanup();
+                _oldAnchorIndex = default;
             }
         }
 

+ 54 - 0
tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs

@@ -1458,6 +1458,60 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(1, raised);
         }
 
+        [Fact]
+        public void Batch_Update_Does_Not_Raise_PropertyChanged_Until_Operation_Finished()
+        {
+            var data = new[] { "foo", "bar", "baz", "qux" };
+            var target = new SelectionModel { Source = data };
+            var raised = 0;
+
+            target.SelectedIndex = new IndexPath(1);
+
+            Assert.Equal(new IndexPath(1), target.AnchorIndex);
+
+            target.PropertyChanged += (s, e) => ++raised;
+
+            using (target.Update())
+            {
+                target.ClearSelection();
+
+                Assert.Equal(0, raised);
+
+                target.AnchorIndex = new IndexPath(2);
+
+                Assert.Equal(0, raised);
+
+                target.SelectedIndex = new IndexPath(3);
+
+                Assert.Equal(0, raised);
+            }
+
+            Assert.Equal(new IndexPath(3), target.AnchorIndex);
+            Assert.Equal(5, raised);
+        }
+
+        [Fact]
+        public void Batch_Update_Does_Not_Raise_PropertyChanged_If_Nothing_Changed()
+        {
+            var data = new[] { "foo", "bar", "baz", "qux" };
+            var target = new SelectionModel { Source = data };
+            var raised = 0;
+
+            target.SelectedIndex = new IndexPath(1);
+
+            Assert.Equal(new IndexPath(1), target.AnchorIndex);
+
+            target.PropertyChanged += (s, e) => ++raised;
+
+            using (target.Update())
+            {
+                target.ClearSelection();
+                target.SelectedIndex = new IndexPath(1);
+            }
+
+            Assert.Equal(0, raised);
+        }
+
         [Fact]
         public void AutoSelect_Selects_When_Enabled()
         {