Browse Source

Restore selection on reset.

Steven Kirk 5 years ago
parent
commit
06390d42ee

+ 3 - 0
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -798,6 +798,9 @@ namespace Avalonia.Controls.Primitives
 
             UpdateContainerSelection();
 
+            _selectedItemsSync ??= new SelectedItemsSync(model);
+            _selectedItemsSync.SelectionModel = model;
+
             if (SelectedIndex != -1)
             {
                 RaiseEvent(new SelectionChangedEventArgs(

+ 27 - 13
src/Avalonia.Controls/Utils/SelectedItemsSync.cs

@@ -34,11 +34,14 @@ namespace Avalonia.Controls.Utils
             get => _selectionModel;
             set
             {
-                value = value ?? throw new ArgumentNullException(nameof(value));
-                UnsubscribeFromSelectionModel(_selectionModel);
-                _selectionModel = value;
-                SubscribeToSelectionModel(_selectionModel);
-                SyncSelectedItemsWithSelectionModel();
+                if (_selectionModel != value)
+                {
+                    value = value ?? throw new ArgumentNullException(nameof(value));
+                    UnsubscribeFromSelectionModel(_selectionModel);
+                    _selectionModel = value;
+                    SubscribeToSelectionModel(_selectionModel);
+                    SyncSelectedItemsWithSelectionModel();
+                }
             }
         }
         
@@ -49,16 +52,19 @@ namespace Avalonia.Controls.Utils
             {
                 value ??= new AvaloniaList<object?>();
 
-                if (value.IsFixedSize)
+                if (_selectedItems != value)
                 {
-                    throw new NotSupportedException(
-                        "Cannot assign fixed size selection to SelectedItems.");
-                }
+                    if (value.IsFixedSize)
+                    {
+                        throw new NotSupportedException(
+                            "Cannot assign fixed size selection to SelectedItems.");
+                    }
 
-                UnsubscribeFromSelectedItems(_selectedItems);
-                _selectedItems = value;
-                SubscribeToSelectedItems(_selectedItems);
-                SyncSelectionModelWithSelectedItems();
+                    UnsubscribeFromSelectedItems(_selectedItems);
+                    _selectedItems = value;
+                    SubscribeToSelectedItems(_selectedItems);
+                    SyncSelectionModelWithSelectedItems();
+                }
             }
         }
 
@@ -226,6 +232,12 @@ namespace Avalonia.Controls.Utils
             }
         }
 
+        private void SelectionModelSourceReset(object sender, EventArgs e)
+        {
+            SyncSelectionModelWithSelectedItems();
+        }
+
+
         private void SubscribeToSelectedItems(IList selectedItems)
         {
             if (selectedItems is INotifyCollectionChanged incc)
@@ -238,6 +250,7 @@ namespace Avalonia.Controls.Utils
         {
             model.PropertyChanged += SelectionModelPropertyChanged;
             model.SelectionChanged += SelectionModelSelectionChanged;
+            model.SourceReset += SelectionModelSourceReset;
         }
 
         private void UnsubscribeFromSelectedItems(IList selectedItems)
@@ -252,6 +265,7 @@ namespace Avalonia.Controls.Utils
         {
             model.PropertyChanged -= SelectionModelPropertyChanged;
             model.SelectionChanged -= SelectionModelSelectionChanged;
+            model.SourceReset -= SelectionModelSourceReset;
         }
 
         private static int IndexOf(object? source, object? item)

+ 33 - 0
tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.Specialized;
 using Avalonia.Collections;
 using Avalonia.Controls.Selection;
 using Avalonia.Controls.Utils;
@@ -230,6 +231,19 @@ namespace Avalonia.Controls.UnitTests.Utils
             Assert.Equal(1, model.SelectedIndex);
         }
 
+        [Fact]
+        public void Restores_Selection_On_Items_Reset()
+        {
+            var items = new ResettingCollection(new[] { "foo", "bar", "baz" });
+            var model = new SelectionModel<string> { Source = items };
+            var target = new SelectedItemsSync(model);
+
+            model.SelectedIndex = 1;
+            items.Reset(new[] { "baz", "foo", "bar" });
+
+            Assert.Equal(2, model.SelectedIndex);
+        }
+
         private static SelectedItemsSync CreateTarget(
             IEnumerable<string> items = null)
         {
@@ -241,5 +255,24 @@ namespace Avalonia.Controls.UnitTests.Utils
             var target = new SelectedItemsSync(model);
             return target;
         }
+
+        private class ResettingCollection : List<string>, INotifyCollectionChanged
+        {
+            public ResettingCollection(IEnumerable<string> items)
+            {
+                AddRange(items);
+            }
+
+            public void Reset(IEnumerable<string> items)
+            {
+                Clear();
+                AddRange(items);
+                CollectionChanged?.Invoke(
+                    this,
+                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+            }
+
+            public event NotifyCollectionChangedEventHandler CollectionChanged;
+        }
     }
 }