Browse Source

Fixed TabItem.ContentTemplate being reused for the next tab item (#17141)

Tom Edwards 1 year ago
parent
commit
1e34a78a83

+ 13 - 2
src/Avalonia.Controls/TabControl.cs

@@ -207,10 +207,21 @@ namespace Avalonia.Controls
                 container ??= ContainerFromIndex(SelectedIndex);
                 if (container != null)
                 {
+                    if (SelectedContentTemplate != SelectContentTemplate(container.GetValue(ContentTemplateProperty)))
+                    {
+                        // If the value of SelectedContentTemplate is about to change, clear it first. This ensures
+                        // that the template is not reused as soon as SelectedContent changes in the statement below
+                        // this block, and also that controls generated from it are unloaded before SelectedContent
+                        // (which is typically their DataContext) changes.
+                        SelectedContentTemplate = null;
+                    }
+
                     _selectedItemSubscriptions = new CompositeDisposable(
                         container.GetObservable(ContentControl.ContentProperty).Subscribe(v => SelectedContent = v),
-                        // Note how we fall back to our own ContentTemplate if the container doesn't specify one
-                        container.GetObservable(ContentControl.ContentTemplateProperty).Subscribe(v => SelectedContentTemplate = v ?? ContentTemplate));
+                        container.GetObservable(ContentControl.ContentTemplateProperty).Subscribe(v => SelectedContentTemplate = SelectContentTemplate(v)));
+
+                    // Note how we fall back to our own ContentTemplate if the container doesn't specify one
+                    IDataTemplate? SelectContentTemplate(IDataTemplate? containerTemplate) => containerTemplate ?? ContentTemplate;
                 }
             }
         }

+ 35 - 0
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@@ -405,6 +405,41 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal("bar", Assert.IsType<TextBlock>(target.ContentPart.Child).Tag);
         }
 
+        [Fact]
+        public void Previous_ContentTemplate_Is_Not_Reused_When_TabItem_Changes()
+        {
+            int templatesBuilt = 0;
+
+            var target = new TabControl
+            {
+                Template = TabControlTemplate(),
+                Items =
+                {
+                    TabItemFactory("First tab content"),
+                    TabItemFactory("Second tab content"),
+                },
+            };
+
+            var root = new TestRoot(target);
+            ApplyTemplate(target);
+
+            target.SelectedIndex = 0;
+            target.SelectedIndex = 1;
+
+            Assert.Equal(2, templatesBuilt);
+
+            TabItem TabItemFactory(object content) => new()
+            {
+                Content = content,
+                ContentTemplate = new FuncDataTemplate<object>((actual, ns) =>
+                {
+                    Assert.Equal(content, actual);
+                    templatesBuilt++;
+                    return new Border();
+                })
+            };
+        }
+
         [Fact]
         public void Should_Not_Propagate_DataContext_To_TabItem_Content()
         {