Browse Source

Make remaining tests pass.

Steven Kirk 5 years ago
parent
commit
9c8376348c

+ 26 - 4
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -351,16 +351,30 @@ namespace Avalonia.Controls.Primitives
         protected override void OnDataContextBeginUpdate()
         {
             base.OnDataContextBeginUpdate();
+            ++_initializing;
 
-            //InternalBeginInit();
+            if (_selection is object)
+            {
+                _selection.Source = null;
+            }
         }
 
         /// <inheritdoc/>
         protected override void OnDataContextEndUpdate()
         {
             base.OnDataContextEndUpdate();
+            --_initializing;
+
+            if (_selection is object && _initializing == 0)
+            {
+                _selection.Source = Items;
 
-            //InternalEndInit();
+                if (Items is null)
+                {
+                    _selection.Clear();
+                    _selectedItemsSync?.SelectedItems?.Clear();
+                }
+            }
         }
 
         protected override void OnInitialized()
@@ -397,9 +411,17 @@ namespace Avalonia.Controls.Primitives
         {
             base.OnPropertyChanged(change);
 
-            if (change.Property == ItemsProperty && _initializing == 0 && _selection is object)
+            if (change.Property == ItemsProperty &&
+                _initializing == 0 &&
+                _selection is object)
             {
-                _selection.Source = change.NewValue.GetValueOrDefault<IEnumerable>();
+                var newValue = change.NewValue.GetValueOrDefault<IEnumerable>();
+                _selection.Source = newValue;
+
+                if (newValue is null)
+                {
+                    _selection.Clear();
+                }
             }
         }
 

+ 1 - 1
src/Avalonia.Controls/Selection/SelectionModel.cs

@@ -44,7 +44,7 @@ namespace Avalonia.Controls.Selection
                         throw new InvalidOperationException("Cannot change source while update is in progress.");
                     }
 
-                    if (base.Source is object)
+                    if (base.Source is object && value is object)
                     {
                         using var update = BatchUpdate();
                         update.Operation.SkipLostSelection = true;

+ 5 - 7
src/Avalonia.Controls/Utils/SelectedItemsSync.cs

@@ -76,20 +76,18 @@ namespace Avalonia.Controls.Utils
 
         private void SyncSelectedItemsWithSelectionModel()
         {
-            if (_selectionModel.Source is null)
-            {
-                return;
-            }
-
             _updatingItems = true;
 
             try
             {
                 _selectedItems.Clear();
 
-                foreach (var i in SelectionModel.SelectedItems)
+                if (_selectionModel.Source is object)
                 {
-                    _selectedItems.Add(i);
+                    foreach (var i in _selectionModel.SelectedItems)
+                    {
+                        _selectedItems.Add(i);
+                    }
                 }
             }
             finally

+ 45 - 1
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@@ -517,7 +517,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
         /// DataContext is in the process of changing.
         /// </remarks>
         [Fact]
-        public void Should_Not_Write_To_Old_DataContext()
+        public void Should_Not_Write_SelectedItems_To_Old_DataContext()
         {
             var vm = new OldDataContextViewModel();
             var target = new TestSelector();
@@ -553,6 +553,46 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Empty(target.SelectedItems);
         }
 
+        /// <summary>
+        /// See <see cref="Should_Not_Write_SelectedItems_To_Old_DataContext"/>.
+        /// </summary>
+        [Fact]
+        public void Should_Not_Write_SelectionModel_To_Old_DataContext()
+        {
+            var vm = new OldDataContextViewModel();
+            var target = new TestSelector();
+
+            var itemsBinding = new Binding
+            {
+                Path = "Items",
+                Mode = BindingMode.OneWay,
+            };
+
+            var selectionBinding = new Binding
+            {
+                Path = "Selection",
+                Mode = BindingMode.OneWay,
+            };
+
+            // Bind Items and Selection to the VM.
+            target.Bind(TestSelector.ItemsProperty, itemsBinding);
+            target.Bind(TestSelector.SelectionProperty, selectionBinding);
+
+            // Set DataContext and SelectedIndex
+            target.DataContext = vm;
+            target.SelectedIndex = 1;
+
+            // Make sure selection is written to selection model
+            Assert.Equal(1, vm.Selection.SelectedIndex);
+
+            // Clear DataContext and ensure that selection is still set in model.
+            target.DataContext = null;
+            Assert.Equal(1, vm.Selection.SelectedIndex);
+
+            // Ensure target's SelectedItems is now clear.
+            Assert.Empty(target.SelectedItems);
+        }
+
         [Fact]
         public void Unbound_SelectedItems_Should_Be_Cleared_When_DataContext_Cleared()
         {
@@ -1459,6 +1499,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
         {
             public static readonly new AvaloniaProperty<IList> SelectedItemsProperty = 
                 SelectingItemsControl.SelectedItemsProperty;
+            public static readonly new DirectProperty<SelectingItemsControl, ISelectionModel> SelectionProperty =
+                SelectingItemsControl.SelectionProperty;
 
             public TestSelector()
             {
@@ -1495,10 +1537,12 @@ namespace Avalonia.Controls.UnitTests.Primitives
             {
                 Items = new List<string> { "foo", "bar" };
                 SelectedItems = new List<string>();
+                Selection = new SelectionModel<string>();
             }
 
             public List<string> Items { get; } 
             public List<string> SelectedItems { get; }
+            public SelectionModel<string> Selection { get; }
         }
 
         private class ItemContainer : Control, ISelectable

+ 20 - 1
tests/Avalonia.Controls.UnitTests/Selection/SelectionModelTests_Single.cs

@@ -175,7 +175,26 @@ namespace Avalonia.Controls.UnitTests.Selection
             }
 
             [Fact]
-            public void Changing_Source_First_Clears_Old_Selection()
+            public void Changing_Source_To_Null_Doesnt_Clear_Selection()
+            {
+                var target = CreateTarget();
+                var raised = 0;
+
+                target.SelectedIndex = 2;
+
+                target.SelectionChanged += (s, e) => ++raised;
+
+                target.Source = null;
+
+                Assert.Equal(2, target.SelectedIndex);
+                Assert.Equal(new[] { 2 }, target.SelectedIndexes);
+                Assert.Null(target.SelectedItem);
+                Assert.Equal(new string?[] { null }, target.SelectedItems);
+                Assert.Equal(0, raised);
+            }
+
+            [Fact]
+            public void Changing_Source_To_NonNUll_First_Clears_Old_Selection()
             {
                 var target = CreateTarget();
                 var raised = 0;