From 6555a51f5ae6986c17ad2da3f767e467712d612a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 14 Jul 2020 22:37:23 +0200 Subject: [PATCH] Fix selection after deleting an item. `SelectionModel` needs to subscribe to `CollectionChanged` on the items before `ItemsControl` in order for the selection to be correct when we come to setting the selected state. Because `SelectionModel.Source` isn't subscribed during initialization in `ItemsChanged`, we also need to make sure we don't subscribe `ItemsControl` to the collection changes during initialization. Instead subscribe in `OnInitialized` (this requires a few tests to be rooted in order to be called). Fixes #4293 --- src/Avalonia.Controls/ItemsControl.cs | 11 +++++++++-- tests/Avalonia.Controls.UnitTests/CarouselTests.cs | 3 +++ .../Avalonia.Controls.UnitTests/ItemsControlTests.cs | 4 ++++ .../Primitives/SelectingItemsControlTests.cs | 4 ++++ .../Primitives/SelectingItemsControlTests_Multiple.cs | 4 ++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 6e0ad66699..da9f619932 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -70,7 +70,6 @@ namespace Avalonia.Controls public ItemsControl() { PseudoClasses.Add(":empty"); - SubscribeToItems(_items); } /// @@ -265,6 +264,11 @@ namespace Avalonia.Controls { } + protected override void OnInitialized() + { + SubscribeToItems(_items); + } + /// /// Handles directional navigation within the . /// @@ -330,7 +334,10 @@ namespace Avalonia.Controls Presenter.Items = newValue; } - SubscribeToItems(newValue); + if (IsInitialized) + { + SubscribeToItems(newValue); + } } /// diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs index a292910fae..c6ca0fb2bf 100644 --- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.LogicalTree; +using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; @@ -155,6 +156,7 @@ namespace Avalonia.Controls.UnitTests IsVirtualized = false }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); @@ -247,6 +249,7 @@ namespace Avalonia.Controls.UnitTests IsVirtualized = false }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 684486cbae..faaa3ed063 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -131,6 +131,7 @@ namespace Avalonia.Controls.UnitTests var child = new Control(); var items = new AvaloniaList(child); + var root = new TestRoot(target); target.Template = GetTemplate(); target.Items = items; items.RemoveAt(0); @@ -283,6 +284,7 @@ namespace Avalonia.Controls.UnitTests var items = new AvaloniaList { "Foo" }; var called = false; + var root = new TestRoot(target); target.Template = GetTemplate(); target.Items = items; target.ApplyTemplate(); @@ -303,6 +305,7 @@ namespace Avalonia.Controls.UnitTests var items = new AvaloniaList { "Foo", "Bar" }; var called = false; + var root = new TestRoot(target); target.Template = GetTemplate(); target.Items = items; target.ApplyTemplate(); @@ -376,6 +379,7 @@ namespace Avalonia.Controls.UnitTests Items = new[] { 1, 2, 3 }, }; + var root = new TestRoot(target); Assert.DoesNotContain(":empty", target.Classes); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 9ef2750ff3..e43e855ae0 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -170,6 +170,8 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected }; + var root = new TestRoot(listBox); + listBox.BeginInit(); listBox.SelectedIndex = 1; @@ -480,6 +482,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Template = Template(), }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); items.Add(new Item { IsSelected = true }); @@ -919,6 +922,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Items = items, }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index dcf25beb50..e9ec8d114f 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -1014,6 +1014,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionMode = SelectionMode.Multiple, }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); @@ -1043,6 +1044,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionMode = SelectionMode.Multiple, }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); @@ -1076,6 +1078,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionMode = SelectionMode.Multiple, }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); @@ -1199,6 +1202,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Template = Template(), }; + var root = new TestRoot(target); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); items.Add(new ItemContainer { IsSelected = true });