diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs index c9d91b1ae6..73c0184ebb 100644 --- a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs +++ b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs @@ -80,10 +80,12 @@ namespace Avalonia.Controls.Selection try { _ignoreSelectedItemsChanges = true; + ++_ignoreModelChanges; base.SetSource(value); } finally { + --_ignoreModelChanges; _ignoreSelectedItemsChanges = false; } @@ -93,17 +95,14 @@ namespace Avalonia.Controls.Selection } else { - foreach (var i in oldSelection) - { - var index = ItemsView!.IndexOf(i); - Select(index); - } + SyncFromSelectedItems(); } } private void SyncToSelectedItems() { - if (_writableSelectedItems is object) + if (_writableSelectedItems is object && + !SequenceEqual(_writableSelectedItems, base.SelectedItems)) { try { @@ -224,6 +223,7 @@ namespace Avalonia.Controls.Selection if (_isResetting) { --_ignoreModelChanges; + _isResetting = false; } } @@ -310,5 +310,27 @@ namespace Avalonia.Controls.Selection return -1; } + + private static bool SequenceEqual(IList first, IReadOnlyList second) + { + if (first is IEnumerable e) + { + return e.SequenceEqual(second); + } + + var comparer = EqualityComparer.Default; + var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + + while (e1.MoveNext()) + { + if (!(e2.MoveNext() && comparer.Equals(e1.Current, e2.Current))) + { + return false; + } + } + + return !e2.MoveNext(); + } } } diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 963bba7c83..7c57e22933 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using System.Linq; using System.Reactive.Subjects; using Avalonia.Collections; @@ -457,6 +458,33 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Initial_Binding_Of_SelectedItems_Should_Not_Cause_Write_To_SelectedItems() + { + var target = new ListBox + { + [!ListBox.ItemsProperty] = new Binding("Items"), + [!ListBox.SelectedItemsProperty] = new Binding("SelectedItems"), + }; + + var viewModel = new + { + Items = new[] { "Foo", "Bar", "Baz " }, + SelectedItems = new ObservableCollection { "Bar" }, + }; + + var raised = 0; + + viewModel.SelectedItems.CollectionChanged += (s, e) => ++raised; + + target.DataContext = viewModel; + + Assert.Equal(0, raised); + Assert.Equal(new[] { "Bar" }, viewModel.SelectedItems); + Assert.Equal(new[] { "Bar" }, target.SelectedItems); + Assert.Equal(new[] { "Bar" }, target.Selection.SelectedItems); + } + private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate((parent, scope) =>