From f9a6f5b1ece28eeb4c584b6928fae493a0dd7394 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Apr 2021 09:20:12 +0200 Subject: [PATCH 1/5] Create failing test for SelectedItems binding. --- .../ListBoxTests.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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) => From 67a1e0921ca68e578ba44289f3a0ddac4d837d6f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Apr 2021 10:00:21 +0200 Subject: [PATCH 2/5] Don't update SelectedItems when changing source. --- .../Selection/InternalSelectionModel.cs | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs index c9d91b1ae6..d0da2f7ce2 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 { @@ -310,5 +309,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(); + } } } From f5df7e6e62339cfc9404e20a0c22835f11b59d52 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 21 Apr 2021 11:48:29 +0100 Subject: [PATCH 3/5] dont obsolete context menu apis. --- src/Avalonia.Controls/Control.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 94ad985046..e964673845 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -37,7 +37,6 @@ namespace Avalonia.Controls /// /// Defines the property. /// - [Obsolete("Prefer ContextFlyout")] public static readonly StyledProperty ContextMenuProperty = AvaloniaProperty.Register(nameof(ContextMenu)); @@ -77,7 +76,6 @@ namespace Avalonia.Controls /// /// Gets or sets a context menu to the control. /// - [Obsolete("Prefer ContextFlyout")] public ContextMenu? ContextMenu { get => GetValue(ContextMenuProperty); From 48fd593147065f22bd14bb95dfc9dca5fbf5b739 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 21 Apr 2021 12:02:56 +0100 Subject: [PATCH 4/5] remove obsolete comments --- src/Avalonia.Themes.Default/TextBox.xaml | 1 - src/Avalonia.Themes.Fluent/Controls/TextBox.xaml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml index 6dce77e96d..29b611279e 100644 --- a/src/Avalonia.Themes.Default/TextBox.xaml +++ b/src/Avalonia.Themes.Default/TextBox.xaml @@ -10,7 +10,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index f882a96408..300a958eb7 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -21,7 +21,6 @@ - From eeb7c8d921fba166276e0b24c4509ed864c7a378 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Apr 2021 13:26:51 +0200 Subject: [PATCH 5/5] Reset _isResetting, d'oh. --- src/Avalonia.Controls/Selection/InternalSelectionModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs index d0da2f7ce2..73c0184ebb 100644 --- a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs +++ b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs @@ -223,6 +223,7 @@ namespace Avalonia.Controls.Selection if (_isResetting) { --_ignoreModelChanges; + _isResetting = false; } }