diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index c8d2c5cc9e..796d108f60 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -36,13 +36,29 @@ namespace Avalonia.Controls get => _rootNode?.Source; set { - using (var operation = new Operation(this)) + var wasNull = _rootNode.Source == null; + + if (_rootNode.Source != null) { - ClearSelection(resetAnchor: true); + using (var operation = new Operation(this)) + { + ClearSelection(resetAnchor: true); + } } _rootNode.Source = value; + RaisePropertyChanged("Source"); + + if (wasNull) + { + var e = new SelectionModelSelectionChangedEventArgs( + null, + SelectedIndices, + null, + SelectedItems); + OnSelectionChanged(e); + } } } diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index 57bf369585..bff84eca92 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -16,30 +16,38 @@ namespace Avalonia.Controls public SelectionModelSelectionChangedEventArgs CreateEventArgs() { - var deselectedCount = 0; - var selectedCount = 0; + var deselectedIndexCount = 0; + var selectedIndexCount = 0; + var deselectedItemCount = 0; + var selectedItemCount = 0; foreach (var change in _changes) { - deselectedCount += change.DeselectedCount; - selectedCount += change.SelectedCount; + deselectedIndexCount += change.DeselectedCount; + selectedIndexCount += change.SelectedCount; + + if (change.Items != null) + { + deselectedItemCount += change.DeselectedCount; + selectedItemCount += change.SelectedCount; + } } var deselectedIndices = new SelectedItems( _changes, - deselectedCount, + deselectedIndexCount, GetDeselectedIndexAt); var selectedIndices = new SelectedItems( _changes, - selectedCount, + selectedIndexCount, GetSelectedIndexAt); var deselectedItems = new SelectedItems( _changes, - deselectedCount, + deselectedItemCount, GetDeselectedItemAt); var selectedItems = new SelectedItems( _changes, - selectedCount, + selectedItemCount, GetSelectedItemAt); return new SelectionModelSelectionChangedEventArgs( @@ -71,7 +79,7 @@ namespace Avalonia.Controls List infos, int index) { - static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; + static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.DeselectedCount : 0; static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; return GetItemAt(infos, index, GetCount, GetRanges); } @@ -80,7 +88,7 @@ namespace Avalonia.Controls List infos, int index) { - static int GetCount(SelectionNodeOperation info) => info.SelectedCount; + static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.SelectedCount : 0; static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; return GetItemAt(infos, index, GetCount, GetRanges); } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 85db801100..04144e1ed0 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -48,8 +48,11 @@ namespace Avalonia.Controls { if (_source != value) { - ClearSelection(); - UnhookCollectionChangedHandler(); + if (_source != null) + { + ClearSelection(); + UnhookCollectionChangedHandler(); + } _source = value; diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index de9fa4f11f..1950da2818 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1424,6 +1424,48 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(2, raised); } + [Fact] + public void Raises_SelectionChanged_With_No_Source() + { + var target = new SelectionModel(); + var raised = 0; + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.Select(1); + + Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void Raises_SelectionChanged_With_Items_After_Source_Is_Set() + { + var target = new SelectionModel(); + var raised = 0; + + target.Select(1); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices); + Assert.Equal(new[] { "bar" }, e.SelectedItems); + ++raised; + }; + + target.Source = new[] { "foo", "bar", "baz" }; + + Assert.Equal(1, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0;