diff --git a/src/Perspex.Controls/ContentControl.cs b/src/Perspex.Controls/ContentControl.cs index e8b52a2948..b6cc7bab95 100644 --- a/src/Perspex.Controls/ContentControl.cs +++ b/src/Perspex.Controls/ContentControl.cs @@ -16,7 +16,7 @@ namespace Perspex.Controls /// /// Displays according to a . /// - public class ContentControl : TemplatedControl, IContentControl + public class ContentControl : TemplatedControl, IContentControl, IContentPresenterHost { /// /// Defines the property. @@ -57,7 +57,7 @@ namespace Perspex.Controls /// /// Gets the presenter from the control's template. /// - public ContentPresenter Presenter + public IContentPresenter Presenter { get; private set; @@ -82,14 +82,9 @@ namespace Perspex.Controls } /// - protected override void OnTemplateApplied(TemplateAppliedEventArgs e) + void IContentPresenterHost.RegisterContentPresenter(IContentPresenter presenter) { - base.OnTemplateApplied(e); - - // We allow ContentControls without ContentPresenters in the template. This can be - // useful for e.g. a simple ToggleButton that displays an image. There's no need to - // have a ContentPresenter in the visual tree for that. - Presenter = e.NameScope.Find("PART_ContentPresenter"); + Presenter = presenter; } } } diff --git a/src/Perspex.Controls/Perspex.Controls.csproj b/src/Perspex.Controls/Perspex.Controls.csproj index a0ea1e4084..92269f9108 100644 --- a/src/Perspex.Controls/Perspex.Controls.csproj +++ b/src/Perspex.Controls/Perspex.Controls.csproj @@ -57,6 +57,7 @@ + diff --git a/src/Perspex.Controls/Presenters/ContentPresenter.cs b/src/Perspex.Controls/Presenters/ContentPresenter.cs index fbc2300d02..223dca8088 100644 --- a/src/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/src/Perspex.Controls/Presenters/ContentPresenter.cs @@ -34,6 +34,7 @@ namespace Perspex.Controls.Presenters static ContentPresenter() { ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); + TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); } /// @@ -146,5 +147,10 @@ namespace Perspex.Controls.Presenters _createdChild = false; InvalidateMeasure(); } + + private void TemplatedParentChanged(PerspexPropertyChangedEventArgs e) + { + (e.NewValue as IContentPresenterHost)?.RegisterContentPresenter(this); + } } } diff --git a/src/Perspex.Controls/Presenters/IContentPresenterHost.cs b/src/Perspex.Controls/Presenters/IContentPresenterHost.cs new file mode 100644 index 0000000000..137742a080 --- /dev/null +++ b/src/Perspex.Controls/Presenters/IContentPresenterHost.cs @@ -0,0 +1,27 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Perspex.Styling; + +namespace Perspex.Controls.Presenters +{ + /// + /// Represents a control which hosts a content presenter. + /// + /// + /// This interface is implemented by which usually contains a + /// and exposes it through its + /// property. ContentPresenters can be within + /// nested templates or in popups and so are not necessarily created immediately when the + /// parent control's template is instantiated so they register themselves using this + /// interface. + /// + public interface IContentPresenterHost : ITemplatedControl + { + /// + /// Registers an with a host control. + /// + /// The content presenter. + void RegisterContentPresenter(IContentPresenter presenter); + } +} diff --git a/tests/Perspex.Controls.UnitTests/ContentControlTests.cs b/tests/Perspex.Controls.UnitTests/ContentControlTests.cs index a8fc9d124c..979a0b4bab 100644 --- a/tests/Perspex.Controls.UnitTests/ContentControlTests.cs +++ b/tests/Perspex.Controls.UnitTests/ContentControlTests.cs @@ -28,7 +28,7 @@ namespace Perspex.Controls.UnitTests target.Content = "Foo"; target.Template = GetTemplate(); target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); var child = ((IVisual)target).VisualChildren.Single(); Assert.IsType(child); @@ -71,7 +71,7 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = child; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); var contentPresenter = child.GetVisualParent(); Assert.Equal(target, contentPresenter.TemplatedParent); @@ -86,7 +86,7 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = child; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Null(child.TemplatedParent); } @@ -117,7 +117,7 @@ namespace Perspex.Controls.UnitTests target.Content = "Foo"; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); var child = target.Presenter.Child; @@ -157,7 +157,7 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = child; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.True(called); } @@ -172,12 +172,12 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = child; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true; target.Content = null; - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.True(called); } @@ -193,7 +193,7 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = child1; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true; @@ -210,13 +210,13 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); target.Content = "Foo"; - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal("Foo", ((TextBlock)target.Presenter.Child).Text); target.Content = "Bar"; - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal("Bar", ((TextBlock)target.Presenter.Child).Text); } @@ -228,7 +228,7 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = "Foo"; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal("Foo", target.Presenter.Child.DataContext); } @@ -241,7 +241,7 @@ namespace Perspex.Controls.UnitTests target.Template = GetTemplate(); target.Content = new TextBlock(); target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Null(target.Presenter.Child.DataContext); } diff --git a/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs b/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs index 57d0a0a7e7..e35f774ef5 100644 --- a/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs +++ b/tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs @@ -28,7 +28,7 @@ namespace Perspex.Controls.UnitTests }; root.ApplyTemplate(); - root.Presenter.UpdateChild(); + ((ContentPresenter)root.Presenter).UpdateChild(); Assert.Same(root.Find("foo"), root.Content); Assert.Same(root.Find("bar"), ((Border)root.Content).Child); @@ -70,7 +70,7 @@ namespace Perspex.Controls.UnitTests root.ApplyTemplate(); - Assert.Null(NameScope.GetNameScope(root.Presenter).Find("foo")); + Assert.Null(NameScope.GetNameScope((Control)root.Presenter).Find("foo")); } private class TestRoot : ContentControl, IRenderRoot, INameScope, IStyleRoot diff --git a/tests/Perspex.Controls.UnitTests/ListBoxTests.cs b/tests/Perspex.Controls.UnitTests/ListBoxTests.cs index eedd1a8682..fd58322b02 100644 --- a/tests/Perspex.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Perspex.Controls.UnitTests/ListBoxTests.cs @@ -42,7 +42,7 @@ namespace Perspex.Controls.UnitTests var text = target.Presenter.Panel.Children .OfType() .Do(x => x.Template = ListBoxItemTemplate()) - .Do(x => { x.ApplyTemplate(); x.Presenter.UpdateChild(); }) + .Do(x => { x.ApplyTemplate(); ((ContentPresenter)x.Presenter).UpdateChild(); }) .Select(x => x.Presenter.Child) .OfType() .Select(x => x.Text) @@ -169,7 +169,7 @@ namespace Perspex.Controls.UnitTests scrollViewer.ApplyTemplate(); // Then make the ScrollViewer create its child. - scrollViewer.Presenter.UpdateChild(); + ((ContentPresenter)scrollViewer.Presenter).UpdateChild(); // Now the ItemsPresenter should be reigstered, so apply its template. target.Presenter.ApplyTemplate(); diff --git a/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs index 4622d77159..47547837f9 100644 --- a/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs +++ b/tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs @@ -231,7 +231,7 @@ namespace Perspex.Controls.UnitTests scrollViewer.ApplyTemplate(); // Then make the ScrollViewer create its child. - scrollViewer.Presenter.UpdateChild(); + ((ContentPresenter)scrollViewer.Presenter).UpdateChild(); // Now the ItemsPresenter should be reigstered, so apply its template. target.Presenter.ApplyTemplate(); diff --git a/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs b/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs index 91b523a299..b8beafa0c4 100644 --- a/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs +++ b/tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System.Linq; +using Moq; using Perspex.Controls.Presenters; using Perspex.Controls.Primitives; using Perspex.Controls.Templates; @@ -12,6 +13,17 @@ namespace Perspex.Controls.UnitTests.Presenters { public class ContentPresenterTests { + [Fact] + public void Should_Register_With_Host_When_TemplatedParent_Set() + { + var host = new Mock(); + var target = new ContentPresenter(); + + target.SetValue(Control.TemplatedParentProperty, host.Object); + + host.Verify(x => x.RegisterContentPresenter(target)); + } + [Fact] public void Setting_Content_To_Control_Should_Set_Child() { diff --git a/tests/Perspex.Controls.UnitTests/ScrollViewerTests.cs b/tests/Perspex.Controls.UnitTests/ScrollViewerTests.cs index f7758d15d2..dba82f976e 100644 --- a/tests/Perspex.Controls.UnitTests/ScrollViewerTests.cs +++ b/tests/Perspex.Controls.UnitTests/ScrollViewerTests.cs @@ -20,7 +20,7 @@ namespace Perspex.Controls.UnitTests }; target.ApplyTemplate(); - target.Presenter.UpdateChild(); + ((ContentPresenter)target.Presenter).UpdateChild(); Assert.IsType(target.Presenter.Child); }