diff --git a/src/Avalonia.Base/Threading/DispatcherPriority.cs b/src/Avalonia.Base/Threading/DispatcherPriority.cs
index ceda1c397f..a2b4b86bac 100644
--- a/src/Avalonia.Base/Threading/DispatcherPriority.cs
+++ b/src/Avalonia.Base/Threading/DispatcherPriority.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Threading
SystemIdle = 1,
///
- /// The job will be processed when the application sis idle.
+ /// The job will be processed when the application is idle.
///
ApplicationIdle = 2,
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index a224ceaadd..d3bd45d13c 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -51,6 +51,7 @@ namespace Avalonia.Controls
SelectableMixin.Attach(IsSelectedProperty);
FocusableProperty.OverrideDefaultValue(true);
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
+ ParentProperty.Changed.AddClassHandler((o, e) => o.OnParentChanged(e));
RequestBringIntoViewEvent.AddClassHandler((x, e) => x.OnRequestBringIntoView(e));
}
@@ -179,5 +180,16 @@ namespace Avalonia.Controls
return logical != null ? result : @default;
}
+
+ private void OnParentChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (!((ILogical)this).IsAttachedToLogicalTree && e.NewValue is null)
+ {
+ // If we're not attached to the logical tree, then OnDetachedFromLogicalTree isn't going to be
+ // called when the item is removed. This results in the item not being removed from the index,
+ // causing #3551. In this case, update the index when Parent is changed to null.
+ ItemContainerGenerator.UpdateIndex();
+ }
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index 32b09e2c47..bd303a81cd 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -1002,6 +1002,35 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(1, child2Node.Presenter.Panel.Children.Count);
}
+ [Fact]
+ public void Clearing_TreeView_Items_Clears_Index()
+ {
+ // Issue #3551
+ var tree = CreateTestTreeData();
+ var target = new TreeView
+ {
+ Template = CreateTreeViewTemplate(),
+ Items = tree,
+ };
+
+ var root = new TestRoot();
+ root.Child = target;
+
+ CreateNodeDataTemplate(target);
+ ApplyTemplates(target);
+
+ var rootNode = tree[0];
+ var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
+
+ Assert.NotNull(container);
+
+ root.Child = null;
+
+ tree.Clear();
+
+ Assert.Empty(target.ItemContainerGenerator.Index.Containers);
+ }
+
private void ApplyTemplates(TreeView tree)
{
tree.ApplyTemplate();