diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs index fcdaf44166..c9d91b1ae6 100644 --- a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs +++ b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs @@ -13,8 +13,9 @@ namespace Avalonia.Controls.Selection internal class InternalSelectionModel : SelectionModel { private IList? _writableSelectedItems; - private bool _ignoreModelChanges; + private int _ignoreModelChanges; private bool _ignoreSelectedItemsChanges; + private bool _isResetting; public InternalSelectionModel() { @@ -130,17 +131,31 @@ namespace Avalonia.Controls.Selection try { - _ignoreModelChanges = true; + ++_ignoreModelChanges; using (BatchUpdate()) { Clear(); - Add(_writableSelectedItems); + + for (var i = 0; i < _writableSelectedItems.Count; ++i) + { + var index = IndexOf(Source, _writableSelectedItems[i]); + + if (index != -1) + { + Select(index); + } + else + { + _writableSelectedItems.RemoveAt(i); + --i; + } + } } } finally { - _ignoreModelChanges = false; + --_ignoreModelChanges; } } @@ -162,7 +177,7 @@ namespace Avalonia.Controls.Selection private void OnSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) { - if (_ignoreModelChanges) + if (_ignoreModelChanges > 0) { return; } @@ -191,6 +206,27 @@ namespace Avalonia.Controls.Selection } } + private protected override void OnSourceCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Reset) + { + ++_ignoreModelChanges; + _isResetting = true; + } + + base.OnSourceCollectionChanged(e); + } + + protected override void OnSourceCollectionChangeFinished() + { + base.OnSourceCollectionChangeFinished(); + + if (_isResetting) + { + --_ignoreModelChanges; + } + } + private void OnSourceReset(object sender, EventArgs e) => SyncFromSelectedItems(); private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -222,7 +258,7 @@ namespace Avalonia.Controls.Selection { using var operation = BatchUpdate(); - _ignoreModelChanges = true; + ++_ignoreModelChanges; switch (e.Action) { @@ -244,7 +280,7 @@ namespace Avalonia.Controls.Selection } finally { - _ignoreModelChanges = false; + --_ignoreModelChanges; } } diff --git a/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs index ce3e698cf3..224b89bf85 100644 --- a/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs @@ -97,6 +97,7 @@ namespace Avalonia.Controls.UnitTests.Selection target.WritableSelectedItems.Clear(); Assert.Empty(target.SelectedIndexes); + Assert.Empty(target.WritableSelectedItems); } [Fact] @@ -123,6 +124,7 @@ namespace Avalonia.Controls.UnitTests.Selection target.WritableSelectedItems = null; Assert.Empty(target.SelectedIndexes); + Assert.Empty(target.WritableSelectedItems); } [Fact] @@ -182,6 +184,7 @@ namespace Avalonia.Controls.UnitTests.Selection target.Source = items; Assert.Equal(1, target.SelectedIndex); + Assert.Equal(new[] { "bar" }, target.WritableSelectedItems); } [Fact] @@ -203,6 +206,7 @@ namespace Avalonia.Controls.UnitTests.Selection items.Reset(new[] { "baz", "foo", "bar" }); Assert.Equal(2, target.SelectedIndex); + Assert.Equal(new[] { "bar" }, target.WritableSelectedItems); } [Fact] @@ -227,6 +231,7 @@ namespace Avalonia.Controls.UnitTests.Selection Assert.Equal(-1, target.SelectedIndex); Assert.Equal(null, target.SelectedItem); + Assert.Empty(target.WritableSelectedItems); Assert.Contains(nameof(target.SelectedIndex), changed); Assert.Contains(nameof(target.SelectedItem), changed); @@ -246,6 +251,7 @@ namespace Avalonia.Controls.UnitTests.Selection Assert.Equal("foo", target.SelectedItem); Assert.Equal(1, target.SelectedIndex); + Assert.Equal(new[] { "foo" }, target.WritableSelectedItems); } [Fact] @@ -257,6 +263,7 @@ namespace Avalonia.Controls.UnitTests.Selection target.Source = new[] { "baz", "foo", "bar" }; Assert.Equal(2, target.SelectedIndex); + Assert.Equal(new[] { "bar" }, target.WritableSelectedItems); } private static InternalSelectionModel CreateTarget(