diff --git a/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
index e2e591215e..5c931bc771 100644
--- a/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
@@ -12,5 +12,10 @@ namespace Avalonia.Controls.Generators
/// Gets the container index for the tree.
///
TreeContainerIndex Index { get; }
+
+ ///
+ /// Updates the index based on the parent .
+ ///
+ void UpdateIndex();
}
}
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index c06a64443c..9200490668 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
@@ -3,8 +3,10 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Avalonia.Controls.Templates;
using Avalonia.Data;
+using Avalonia.LogicalTree;
namespace Avalonia.Controls.Generators
{
@@ -15,6 +17,8 @@ namespace Avalonia.Controls.Generators
public class TreeItemContainerGenerator : ItemContainerGenerator, ITreeItemContainerGenerator
where T : class, IControl, new()
{
+ private TreeView _treeView;
+
///
/// Initializes a new instance of the class.
///
@@ -23,31 +27,28 @@ namespace Avalonia.Controls.Generators
/// The container's ContentTemplate property.
/// The container's Items property.
/// The container's IsExpanded property.
- /// The container index for the tree
public TreeItemContainerGenerator(
IControl owner,
AvaloniaProperty contentProperty,
AvaloniaProperty contentTemplateProperty,
AvaloniaProperty itemsProperty,
- AvaloniaProperty isExpandedProperty,
- TreeContainerIndex index)
+ AvaloniaProperty isExpandedProperty)
: base(owner, contentProperty, contentTemplateProperty)
{
Contract.Requires(owner != null);
Contract.Requires(contentProperty != null);
Contract.Requires(itemsProperty != null);
Contract.Requires(isExpandedProperty != null);
- Contract.Requires(index != null);
ItemsProperty = itemsProperty;
IsExpandedProperty = isExpandedProperty;
- Index = index;
+ UpdateIndex();
}
///
/// Gets the container index for the tree.
///
- public TreeContainerIndex Index { get; }
+ public TreeContainerIndex Index { get; private set; }
///
/// Gets the item container's Items property.
@@ -70,7 +71,7 @@ namespace Avalonia.Controls.Generators
}
else if (container != null)
{
- Index.Add(item, container);
+ Index?.Add(item, container);
return container;
}
else
@@ -92,7 +93,7 @@ namespace Avalonia.Controls.Generators
result.DataContext = item;
}
- Index.Add(item, result);
+ Index?.Add(item, result);
return result;
}
@@ -101,24 +102,50 @@ namespace Avalonia.Controls.Generators
public override IEnumerable Clear()
{
var items = base.Clear();
- Index.Remove(0, items);
+ Index?.Remove(0, items);
return items;
}
public override IEnumerable Dematerialize(int startingIndex, int count)
{
- Index.Remove(startingIndex, GetContainerRange(startingIndex, count));
+ Index?.Remove(startingIndex, GetContainerRange(startingIndex, count));
return base.Dematerialize(startingIndex, count);
}
public override IEnumerable RemoveRange(int startingIndex, int count)
{
- Index.Remove(startingIndex, GetContainerRange(startingIndex, count));
+ Index?.Remove(startingIndex, GetContainerRange(startingIndex, count));
return base.RemoveRange(startingIndex, count);
}
public override bool TryRecycle(int oldIndex, int newIndex, object item) => false;
+ public void UpdateIndex()
+ {
+ if (Owner is TreeView treeViewOwner && Index == null)
+ {
+ Index = new TreeContainerIndex();
+ _treeView = treeViewOwner;
+ }
+ else if (Owner.IsAttachedToLogicalTree)
+ {
+ var treeView = Owner.GetSelfAndLogicalAncestors().OfType().FirstOrDefault();
+
+ if (treeView != _treeView)
+ {
+ Clear();
+ Index = treeView?.ItemContainerGenerator?.Index;
+ _treeView = treeView;
+ }
+ }
+ else
+ {
+ Clear();
+ Index = null;
+ _treeView = null;
+ }
+ }
+
class WrapperTreeDataTemplate : ITreeDataTemplate
{
private readonly IDataTemplate _inner;
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 59844be8a6..738d9d0b51 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -393,8 +393,7 @@ namespace Avalonia.Controls
TreeViewItem.HeaderProperty,
TreeViewItem.ItemTemplateProperty,
TreeViewItem.ItemsProperty,
- TreeViewItem.IsExpandedProperty,
- new TreeContainerIndex());
+ TreeViewItem.IsExpandedProperty);
result.Index.Materialized += ContainerMaterialized;
return result;
}
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index 07d5497c14..4d24337c3a 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -98,17 +98,18 @@ namespace Avalonia.Controls
TreeViewItem.HeaderProperty,
TreeViewItem.ItemTemplateProperty,
TreeViewItem.ItemsProperty,
- TreeViewItem.IsExpandedProperty,
- _treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex());
+ TreeViewItem.IsExpandedProperty);
}
///
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
+
_treeView = this.GetLogicalAncestors().OfType().FirstOrDefault();
-
+
Level = CalculateDistanceFromLogicalParent(this) - 1;
+ ItemContainerGenerator.UpdateIndex();
if (ItemTemplate == null && _treeView?.ItemTemplate != null)
{
@@ -119,7 +120,7 @@ namespace Avalonia.Controls
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
- ItemContainerGenerator.Clear();
+ ItemContainerGenerator.UpdateIndex();
}
protected virtual void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index a91b7a0701..ed8a39d063 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -10,6 +10,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Core;
+using Avalonia.Diagnostics;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
@@ -33,6 +34,8 @@ namespace Avalonia.Controls.UnitTests
Items = CreateTestTreeData(),
};
+ var root = new TestRoot(target);
+
CreateNodeDataTemplate(target);
ApplyTemplates(target);
@@ -77,6 +80,8 @@ namespace Avalonia.Controls.UnitTests
Items = CreateTestTreeData(),
};
+ var root = new TestRoot(target);
+
CreateNodeDataTemplate(target);
ApplyTemplates(target);
@@ -527,6 +532,8 @@ namespace Avalonia.Controls.UnitTests
Items = data,
};
+ var root = new TestRoot(target);
+
CreateNodeDataTemplate(target);
ApplyTemplates(target);
@@ -893,6 +900,37 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(2, GetItem(target, 0, 1, 0).Level);
}
+ [Fact]
+ public void Adding_Node_To_Removed_And_ReAdded_Parent_Should_Not_Crash()
+ {
+ // Issue #2985
+ var tree = CreateTestTreeData();
+ var target = new TreeView
+ {
+ Template = CreateTreeViewTemplate(),
+ Items = tree,
+ };
+
+ var visualRoot = new TestRoot();
+ visualRoot.Child = target;
+
+ CreateNodeDataTemplate(target);
+ ApplyTemplates(target);
+ ExpandAll(target);
+
+ var parent = tree[0];
+ var node = parent.Children[1];
+
+ parent.Children.Remove(node);
+ parent.Children.Add(node);
+
+ var item = target.ItemContainerGenerator.Index.ContainerFromItem(node);
+ ApplyTemplates(new[] { item });
+
+ // #2985 causes ArgumentException here.
+ node.Children.Add(new Node());
+ }
+
[Fact]
public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection()
{