|
|
@@ -1,6 +1,7 @@
|
|
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
|
|
+using System;
|
|
|
using System.Collections.Specialized;
|
|
|
using System.Linq;
|
|
|
using Moq;
|
|
|
@@ -11,6 +12,9 @@ using Avalonia.Styling;
|
|
|
using Avalonia.UnitTests;
|
|
|
using Avalonia.VisualTree;
|
|
|
using Xunit;
|
|
|
+using Avalonia.Markup.Xaml.Data;
|
|
|
+using Avalonia.Data;
|
|
|
+using System.Collections.Generic;
|
|
|
|
|
|
namespace Avalonia.Controls.UnitTests
|
|
|
{
|
|
|
@@ -273,6 +277,60 @@ namespace Avalonia.Controls.UnitTests
|
|
|
Assert.Null(target.Presenter.Child.DataContext);
|
|
|
}
|
|
|
|
|
|
+ [Fact]
|
|
|
+ public void Binding_ContentTemplate_After_Content_Does_Not_Leave_Orpaned_TextBlock()
|
|
|
+ {
|
|
|
+ // Test for #1271.
|
|
|
+ var children = new List<IControl>();
|
|
|
+ var presenter = new ContentPresenter();
|
|
|
+
|
|
|
+ // The content and then the content template property need to be bound with delayed bindings
|
|
|
+ // as they are in Avalonia.Markup.Xaml.
|
|
|
+ DelayedBinding.Add(presenter, ContentPresenter.ContentProperty, new Binding("Content")
|
|
|
+ {
|
|
|
+ Priority = BindingPriority.TemplatedParent,
|
|
|
+ RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
|
|
|
+ });
|
|
|
+
|
|
|
+ DelayedBinding.Add(presenter, ContentPresenter.ContentTemplateProperty, new Binding("ContentTemplate")
|
|
|
+ {
|
|
|
+ Priority = BindingPriority.TemplatedParent,
|
|
|
+ RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
|
|
|
+ });
|
|
|
+
|
|
|
+ presenter.GetObservable(ContentPresenter.ChildProperty).Subscribe(children.Add);
|
|
|
+
|
|
|
+ var target = new ContentControl
|
|
|
+ {
|
|
|
+ Template = new FuncControlTemplate<ContentControl>(_ => presenter),
|
|
|
+ ContentTemplate = new FuncDataTemplate<string>(x => new Canvas()),
|
|
|
+ Content = "foo",
|
|
|
+ };
|
|
|
+
|
|
|
+ // The control must be rooted.
|
|
|
+ var root = new TestRoot
|
|
|
+ {
|
|
|
+ Child = target,
|
|
|
+ };
|
|
|
+
|
|
|
+ target.ApplyTemplate();
|
|
|
+
|
|
|
+ // When the template is applied, the Content property is bound before the ContentTemplate
|
|
|
+ // property, causing a TextBlock to be created by the default template before ContentTemplate
|
|
|
+ // is bound.
|
|
|
+ Assert.Collection(
|
|
|
+ children,
|
|
|
+ x => Assert.Null(x),
|
|
|
+ x => Assert.IsType<TextBlock>(x),
|
|
|
+ x => Assert.IsType<Canvas>(x));
|
|
|
+
|
|
|
+ var textBlock = (TextBlock)children[1];
|
|
|
+
|
|
|
+ // The leak in #1271 was caused by the TextBlock's logical parent not being cleared when
|
|
|
+ // it is replaced by the Canvas.
|
|
|
+ Assert.Null(textBlock.GetLogicalParent());
|
|
|
+ }
|
|
|
+
|
|
|
private FuncControlTemplate GetTemplate()
|
|
|
{
|
|
|
return new FuncControlTemplate<ContentControl>(parent =>
|