diff --git a/Avalonia.Controls.TreeDataGrid.sln b/Avalonia.Controls.TreeDataGrid.sln index 24d5d5c6..92277cf0 100644 --- a/Avalonia.Controls.TreeDataGrid.sln +++ b/Avalonia.Controls.TreeDataGrid.sln @@ -45,9 +45,7 @@ Global {9A36E37E-2C03-4B5A-B7EE-A91DC95C3E4A}.Release|Any CPU.ActiveCfg = Release|Any CPU {9A36E37E-2C03-4B5A-B7EE-A91DC95C3E4A}.Release|Any CPU.Build.0 = Release|Any CPU {D45C7B46-A12C-4412-8397-B51B75A09999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D45C7B46-A12C-4412-8397-B51B75A09999}.Debug|Any CPU.Build.0 = Debug|Any CPU {D45C7B46-A12C-4412-8397-B51B75A09999}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D45C7B46-A12C-4412-8397-B51B75A09999}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs index b95dd152..28aaf1a1 100644 --- a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs +++ b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs @@ -429,6 +429,7 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e EffectiveViewportChanged -= OnEffectiveViewportChanged; UnsubscribeFromItemChanges(); + RecycleAllElements(); } protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) diff --git a/tests/Avalonia.Controls.TreeDataGrid.Tests/Primitives/TreeDataGridRowsPresenterTests.cs b/tests/Avalonia.Controls.TreeDataGrid.Tests/Primitives/TreeDataGridRowsPresenterTests.cs index d3822bd7..dbf18057 100644 --- a/tests/Avalonia.Controls.TreeDataGrid.Tests/Primitives/TreeDataGridRowsPresenterTests.cs +++ b/tests/Avalonia.Controls.TreeDataGrid.Tests/Primitives/TreeDataGridRowsPresenterTests.cs @@ -3,7 +3,10 @@ using System.Linq; using Avalonia.Collections; using Avalonia.Controls.Models.TreeDataGrid; +using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Data; using Avalonia.Headless.XUnit; using Avalonia.LogicalTree; using Avalonia.Media; @@ -423,6 +426,53 @@ public void Handles_Moving_Focused_Row_While_Outside_Viewport() // The correct element should be shown. Assert.Same(items[0], target.RealizedElements.ElementAt(0)!.DataContext); } + + [AvaloniaFact(Timeout = 10000)] + public void Handles_Adding_Rows_While_Detached_From_VisualTree() + { + var (target, scroll, items) = CreateTarget(itemCount: 5); + var testWindow = scroll.Parent as TestWindow; + + if (testWindow != null) + { + testWindow.Content = null; + testWindow.UpdateLayout(); + } + + var tabItem = new TabItem { Content = scroll }; + var tabControl = new TabControl { Items = { tabItem, new TabItem() }, Template = TabControlTemplate() }; + + ApplyTemplate(tabControl); + + if (testWindow != null) + { + testWindow.Content = tabControl; + tabControl.ApplyTemplate(); + testWindow.UpdateLayout(); + } + + Dispatcher.UIThread.RunJobs(); + + tabControl.SelectedIndex = 1; + + Layout(target); + + Enumerable.Range(5, 5).ToList().ForEach(x => items.Insert(1, new Model { Id = x, Title = "Item " + x })); + + tabControl.SelectedIndex = 0; + Layout(target); + + var indexes = GetRealizedRowIndexes(target); + var models = target!.RealizedElements + .Cast().Select(x => x?.Model) + .Cast().ToList(); + + var distinctModelCount = models.DistinctBy(x => x.Id).Count(); + + Assert.Equal(10, indexes.Count); + Assert.Equal(10, models.Count); + Assert.Equal(10, distinctModelCount); + } [AvaloniaFact(Timeout = 10000)] public void Updates_Star_Column_ActualWidth() @@ -615,6 +665,55 @@ private static void Layout(TreeDataGridRowsPresenter target) { target.UpdateLayout(); } + + private static void ApplyTemplate(TabControl target) + { + target.ApplyTemplate(); + + target.Presenter?.ApplyTemplate(); + + foreach (var tabItem in target.GetLogicalChildren().OfType()) + { + tabItem.Template = TabItemTemplate(); + + tabItem.ApplyTemplate(); + + tabItem.Presenter?.UpdateChild(); + } + } + + private static IControlTemplate TabItemTemplate() + { + return new FuncControlTemplate((parent, scope) => + new ContentPresenter + { + Name = "PART_ContentPresenter", + [~ContentPresenter.ContentProperty] = new TemplateBinding(TabItem.HeaderProperty), + [~ContentPresenter.ContentTemplateProperty] = new TemplateBinding(TabItem.HeaderTemplateProperty), + RecognizesAccessKey = true, + }.RegisterInNameScope(scope)); + } + + private static IControlTemplate TabControlTemplate() + { + return new FuncControlTemplate((parent, scope) => + new StackPanel + { + Children = + { + new ItemsPresenter + { + Name = "PART_ItemsPresenter", + }.RegisterInNameScope(scope), + new ContentPresenter + { + Name = "PART_SelectedContentHost", + [~ContentPresenter.ContentProperty] = new TemplateBinding(TabControl.SelectedContentProperty), + [~ContentPresenter.ContentTemplateProperty] = new TemplateBinding(TabControl.SelectedContentTemplateProperty), + }.RegisterInNameScope(scope) + } + }); + } private class Model {