From 939d80788b94829801a0a909b711050631e86e6d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 9 Aug 2019 14:16:05 +0200 Subject: [PATCH 1/4] Added failing test for #2823. --- .../ContentPresenterTests_InTemplate.cs | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs index 952180d21b..6ab9c345d4 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs @@ -1,7 +1,11 @@ // 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.Generic; +using System.ComponentModel; using System.Linq; +using System.Reactive.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; @@ -256,7 +260,6 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.IsType(target.Child); } - [Fact] public void Should_Not_Bind_Old_Child_To_New_DataContext() { @@ -281,6 +284,34 @@ namespace Avalonia.Controls.UnitTests.Presenters target.Content = 42; } + [Fact] + public void Should_Not_Bind_Child_To_Wrong_DataContext_When_Removing() + { + // Test for issue #2823 + var canvas = new Canvas(); + var (target, host) = CreateTarget(); + var viewModel = new TestViewModel { Content = "foo" }; + var dataContexts = new List(); + + target.Bind(ContentPresenter.ContentProperty, (IBinding)new TemplateBinding(ContentControl.ContentProperty)); + canvas.GetObservable(ContentPresenter.DataContextProperty).Subscribe(x => dataContexts.Add(x)); + + host.DataTemplates.Add(new FuncDataTemplate((_, __) => canvas)); + host.Bind(ContentControl.ContentProperty, new Binding(nameof(TestViewModel.Content))); + host.DataContext = viewModel; + + Assert.Same(canvas, target.Child); + + viewModel.Content = 42; + + Assert.Equal(new object[] + { + null, + "foo", + null, + }, dataContexts); + } + [Fact] public void Should_Set_InheritanceParent_Even_When_LogicalParent_Is_Already_Set() { @@ -333,5 +364,25 @@ namespace Avalonia.Controls.UnitTests.Presenters { public IControl Child { get; set; } } + + private class TestViewModel : INotifyPropertyChanged + { + private object _content; + + public object Content + { + get => _content; + set + { + if (_content != value) + { + _content = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Content))); + } + } + } + + public event PropertyChangedEventHandler PropertyChanged; + } } } From 1dec99ab693258c02adbab1dd440d279cac1368c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 9 Aug 2019 14:24:23 +0200 Subject: [PATCH 2/4] Reset `InheritanceParent` in ContentControlMixin. Doing it where we were doing it before caused #2823. --- src/Avalonia.Controls/Mixins/ContentControlMixin.cs | 1 + src/Avalonia.Controls/Presenters/ContentPresenter.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Mixins/ContentControlMixin.cs b/src/Avalonia.Controls/Mixins/ContentControlMixin.cs index 25b29e37e6..b826fb982e 100644 --- a/src/Avalonia.Controls/Mixins/ContentControlMixin.cs +++ b/src/Avalonia.Controls/Mixins/ContentControlMixin.cs @@ -150,6 +150,7 @@ namespace Avalonia.Controls.Mixins if (oldValue is IControl child) { logicalChildren.Remove(child); + ((ISetInheritanceParent)child).SetParent(child.Parent); } child = newValue as IControl; diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index e2e73bd465..c2690d503d 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -229,7 +229,6 @@ namespace Avalonia.Controls.Presenters if (oldChild != null) { VisualChildren.Remove(oldChild); - ((ISetInheritanceParent)oldChild).SetParent(oldChild.Parent); } if (oldChild?.Parent == this) From 947598fcec9d492831016b813fca538fd8246d4b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 9 Aug 2019 14:52:32 +0200 Subject: [PATCH 3/4] More tests forContentPresenter and InheritanceParent. Make sure the inheritance parent is reset for non-rooted and standalone `ContentPresenter`s. --- .../Presenters/ContentPresenter.cs | 7 ++++++- .../ContentPresenterTests_Standalone.cs | 16 ++++++++++++++++ .../ContentPresenterTests_Unrooted.cs | 17 +++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index c2690d503d..1072b21b1b 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -237,7 +237,7 @@ namespace Avalonia.Controls.Presenters // template. LogicalChildren.Remove(oldChild); } - else + else if (TemplatedParent != null) { // If we're in a ContentControl's template then invoke ChildChanging to let // ContentControlMixin handle removing the logical child. @@ -248,6 +248,10 @@ namespace Avalonia.Controls.Presenters newChild, BindingPriority.LocalValue)); } + else if (oldChild != null) + { + ((ISetInheritanceParent)oldChild).SetParent(oldChild.Parent); + } } // Set the DataContext if the data isn't a control. @@ -433,6 +437,7 @@ namespace Avalonia.Controls.Presenters { VisualChildren.Remove(Child); LogicalChildren.Remove(Child); + ((ISetInheritanceParent)Child).SetParent(Child.Parent); Child = null; _dataTemplate = null; } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs index ab75a87110..59f3ae44c2 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs @@ -251,5 +251,21 @@ namespace Avalonia.Controls.UnitTests.Presenters target.Content = 42; } + + [Fact] + public void Should_Reset_InheritanceParent_When_Child_Removed() + { + var logicalParent = new Canvas(); + var child = new TextBlock(); + var target = new ContentPresenter(); + var root = new TestRoot(target); + + ((ISetLogicalParent)child).SetParent(logicalParent); + target.Content = child; + target.Content = null; + + // InheritanceParent is exposed via StylingParent. + Assert.Same(logicalParent, ((IStyledElement)child).StylingParent); + } } } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs index 09970926fa..c30e81a1cb 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs @@ -98,5 +98,22 @@ namespace Avalonia.Controls.UnitTests.Presenters target.ApplyTemplate(); Assert.IsType(target.Child); } + + [Fact] + public void Should_Reset_InheritanceParent_When_Child_Removed() + { + var logicalParent = new Canvas(); + var child = new TextBlock(); + var target = new ContentPresenter(); + + ((ISetLogicalParent)child).SetParent(logicalParent); + target.Content = child; + target.UpdateChild(); + target.Content = null; + target.UpdateChild(); + + // InheritanceParent is exposed via StylingParent. + Assert.Same(logicalParent, ((IStyledElement)child).StylingParent); + } } } From b225e84324016944dfadbf82f8207fe687939cae Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 9 Aug 2019 14:24:12 +0300 Subject: [PATCH 4/4] Various control catalog improvements --- samples/ControlCatalog/MainView.xaml | 24 ++++++++++++++---- samples/ControlCatalog/MainWindow.xaml | 1 + samples/ControlCatalog/Pages/PointersPage.cs | 9 +++++++ samples/ControlCatalog/Pages/ScreenPage.cs | 5 +++- samples/ControlCatalog/SideBar.xaml | 26 +++++++++++++------- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 5f24c8062e..c35f8a3c0c 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -6,10 +6,13 @@ Foreground="{DynamicResource ThemeForegroundBrush}" FontSize="{DynamicResource FontSizeNormal}"> - - Light - Dark - + + + @@ -21,7 +24,12 @@ - + + + + @@ -42,6 +50,12 @@ + + + Light + Dark + + diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 6a9e865e26..9527ac3b4e 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -1,4 +1,5 @@  + Background="{TemplateBinding Background}" + DockPanel.Dock="Left"> - - + + + + + @@ -58,6 +64,8 @@ + +