diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 0d5a453141..ef10dcdb22 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -293,6 +293,23 @@ namespace Avalonia.Controls return TreeItemFromContainer(this, container); } + private protected override void OnItemsViewCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + base.OnItemsViewCollectionChanged(sender, e); + + switch (e.Action) + { + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Replace: + foreach (var i in e.OldItems!) + SelectedItems.Remove(i); + break; + case NotifyCollectionChangedAction.Reset: + SelectedItems.Clear(); + break; + } + } + /// /// Subscribes to the CollectionChanged event, if any. /// diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index ccd4f89204..cf12c12447 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; +using Avalonia.Controls.Selection; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Input; @@ -289,6 +291,30 @@ namespace Avalonia.Controls } } + private protected override void OnItemsViewCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + base.OnItemsViewCollectionChanged(sender, e); + + if (_treeView is null) + return; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Replace: + foreach (var i in e.OldItems!) + _treeView.SelectedItems.Remove(i); + break; + case NotifyCollectionChangedAction.Reset: + foreach (var i in GetRealizedContainers()) + { + if (i is TreeViewItem tvi && tvi.IsSelected) + _treeView.SelectedItems.Remove(i.DataContext); + } + break; + } + } + private static int CalculateDistanceFromLogicalParent(ILogical? logical, int @default = -1) where T : class { var result = 0; diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 7d290b1ec0..51300f343a 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -724,6 +724,86 @@ namespace Avalonia.Controls.UnitTests Assert.True(called); } + [Fact] + public void Removing_Selected_Root_Item_Should_Clear_Selection() + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0]; + + target.SelectedItem = item; + + data.RemoveAt(0); + + Assert.Null(target.SelectedItem); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void Resetting_Root_Items_Should_Clear_Selection() + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0]; + + target.SelectedItem = item; + + data.Clear(); + + Assert.Null(target.SelectedItem); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void Removing_Selected_Child_Item_Should_Clear_Selection() + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0].Children[1]; + + target.SelectedItem = item; + + data[0].Children.RemoveAt(1); + + Assert.Null(target.SelectedItem); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void Replacing_Selected_Child_Item_Should_Clear_Selection() + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0].Children[1]; + + target.SelectedItem = item; + + data[0].Children[1] = new Node(); + + Assert.Null(target.SelectedItem); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void Clearing_Child_Items_Should_Clear_Selection() + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0].Children[1]; + + target.SelectedItem = item; + + data[0].Children.Clear(); + + Assert.Null(target.SelectedItem); + Assert.Empty(target.SelectedItems); + } + [Fact] public void SelectedItem_Should_Be_Valid_When_SelectedItemChanged_Event_Raised() {