From 309ac30a418fbeb85cb3d74dcabc0b4f67301944 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 24 Dec 2014 14:53:12 +0000 Subject: [PATCH] Make ContentControl child be logical child. The control this is created by the ContentPresenter now appears as the logical child of the parent ContentControl. --- .../ContentControlTests.cs | 36 ++++++++++++++ Perspex.Controls/ContentControl.cs | 20 +++++++- .../Presenters/ContentPresenter.cs | 47 +++++++++++++++---- Perspex.Themes.Default/ContentControlStyle.cs | 3 +- Perspex.Themes.Default/WindowStyle.cs | 1 + 5 files changed, 96 insertions(+), 11 deletions(-) diff --git a/Perspex.Controls.UnitTests/ContentControlTests.cs b/Perspex.Controls.UnitTests/ContentControlTests.cs index 5731afa76d..f4592aac32 100644 --- a/Perspex.Controls.UnitTests/ContentControlTests.cs +++ b/Perspex.Controls.UnitTests/ContentControlTests.cs @@ -125,11 +125,28 @@ namespace Perspex.Controls.UnitTests var target = new ContentControl(); var child = new Control(); + target.Template = this.GetTemplate(); target.Content = child; + target.ApplyTemplate(); CollectionAssert.AreEqual(new[] { child }, ((ILogical)target).LogicalChildren.ToList()); } + [TestMethod] + public void Setting_Content_To_String_Should_Make_TextBlock_Appear_In_LogicalChildren() + { + var target = new ContentControl(); + var child = new Control(); + + target.Template = this.GetTemplate(); + target.Content = "Foo"; + target.ApplyTemplate(); + + var logical = (ILogical)target; + Assert.AreEqual(1, logical.LogicalChildren.Count); + Assert.IsInstanceOfType(logical.LogicalChildren[0], typeof(TextBlock)); + } + [TestMethod] public void Clearing_Content_Should_Remove_From_LogicalChildren() { @@ -152,7 +169,13 @@ namespace Perspex.Controls.UnitTests ((ILogical)contentControl).LogicalChildren.CollectionChanged += (s, e) => called = e.Action == NotifyCollectionChangedAction.Add; + contentControl.Template = this.GetTemplate(); contentControl.Content = child; + contentControl.ApplyTemplate(); + + // Need to call ApplyTemplate on presenter for CollectionChanged to be called. + var presenter = contentControl.GetTemplateControls().Single(x => x.Id == "presenter"); + presenter.ApplyTemplate(); Assert.IsTrue(called); } @@ -164,13 +187,19 @@ namespace Perspex.Controls.UnitTests var child = new Control(); var called = false; + contentControl.Template = this.GetTemplate(); contentControl.Content = child; + ApplyTemplate(contentControl); ((ILogical)contentControl).LogicalChildren.CollectionChanged += (s, e) => called = e.Action == NotifyCollectionChangedAction.Remove; contentControl.Content = null; + // Need to call ApplyTemplate on presenter for CollectionChanged to be called. + var presenter = contentControl.GetTemplateControls().Single(x => x.Id == "presenter"); + presenter.ApplyTemplate(); + Assert.IsTrue(called); } @@ -182,13 +211,19 @@ namespace Perspex.Controls.UnitTests var child2 = new Control(); var called = false; + contentControl.Template = this.GetTemplate(); contentControl.Content = child1; + contentControl.ApplyTemplate(); ((ILogical)contentControl).LogicalChildren.CollectionChanged += (s, e) => called = e.Action == NotifyCollectionChangedAction.Replace; contentControl.Content = child2; + // Need to call ApplyTemplate on presenter for CollectionChanged to be called. + var presenter = contentControl.GetTemplateControls().Single(x => x.Id == "presenter"); + presenter.ApplyTemplate(); + Assert.IsTrue(called); } @@ -206,6 +241,7 @@ namespace Perspex.Controls.UnitTests Background = new Perspex.Media.SolidColorBrush(0xffffffff), Content = new ContentPresenter { + Id = "presenter", [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty], } }; diff --git a/Perspex.Controls/ContentControl.cs b/Perspex.Controls/ContentControl.cs index df9ec3a8ca..decaf5158a 100644 --- a/Perspex.Controls/ContentControl.cs +++ b/Perspex.Controls/ContentControl.cs @@ -8,6 +8,7 @@ namespace Perspex.Controls { using System; using Perspex.Collections; + using Perspex.Controls.Presenters; using Perspex.Controls.Primitives; using Perspex.Layout; @@ -24,6 +25,10 @@ namespace Perspex.Controls private SingleItemPerspexList logicalChild = new SingleItemPerspexList(); + private ContentPresenter presenter; + + private IDisposable presenterSubscription; + public ContentControl() { this.GetObservableWithHistory(ContentProperty).Subscribe(x => @@ -40,8 +45,6 @@ namespace Perspex.Controls { control2.Parent = this; } - - this.logicalChild.SingleItem = control2; }); } @@ -67,5 +70,18 @@ namespace Perspex.Controls get { return this.GetValue(VerticalContentAlignmentProperty); } set { this.SetValue(VerticalContentAlignmentProperty, value); } } + + protected override void OnTemplateApplied() + { + if (this.presenterSubscription != null) + { + this.presenterSubscription.Dispose(); + this.presenterSubscription = null; + } + + this.presenter = this.GetTemplateChild("presenter"); + this.presenterSubscription = this.presenter.ChildObservable + .Subscribe(x => this.logicalChild.SingleItem = x); + } } } diff --git a/Perspex.Controls/Presenters/ContentPresenter.cs b/Perspex.Controls/Presenters/ContentPresenter.cs index 617489ad67..4646348df4 100644 --- a/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/Perspex.Controls/Presenters/ContentPresenter.cs @@ -9,8 +9,8 @@ namespace Perspex.Controls.Presenters using System; using System.Linq; using System.Reactive.Linq; + using System.Reactive.Subjects; using Perspex.Controls.Primitives; - using Perspex.Layout; public class ContentPresenter : Control, IVisual { @@ -19,11 +19,44 @@ namespace Perspex.Controls.Presenters private bool createdChild; + private Control child; + + private BehaviorSubject childObservable = new BehaviorSubject(null); + public ContentPresenter() { this.GetObservable(ContentProperty).Skip(1).Subscribe(this.ContentChanged); } + public Control Child + { + get + { + return this.Child; + } + + private set + { + if (this.child != value) + { + this.ClearVisualChildren(); + this.child = value; + + if (value != null) + { + this.AddVisualChild(value); + } + + this.childObservable.OnNext(value); + } + } + } + + public IObservable ChildObservable + { + get { return this.childObservable; } + } + public object Content { get { return this.GetValue(ContentProperty); } @@ -45,12 +78,10 @@ namespace Perspex.Controls.Presenters protected override Size MeasureOverride(Size availableSize) { - Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; - - if (child != null) + if (this.child != null) { - child.Measure(availableSize); - return child.DesiredSize.Value; + this.child.Measure(availableSize); + return this.child.DesiredSize.Value; } return new Size(); @@ -64,13 +95,13 @@ namespace Perspex.Controls.Presenters private void CreateChild() { + Control result = null; object content = this.Content; this.ClearVisualChildren(); if (content != null) { - Control result; if (content is Control) { @@ -101,9 +132,9 @@ namespace Perspex.Controls.Presenters } result.TemplatedParent = foo; - this.AddVisualChild(result); } + this.Child = result; this.createdChild = true; } } diff --git a/Perspex.Themes.Default/ContentControlStyle.cs b/Perspex.Themes.Default/ContentControlStyle.cs index 8afb594a5e..1dac4417f9 100644 --- a/Perspex.Themes.Default/ContentControlStyle.cs +++ b/Perspex.Themes.Default/ContentControlStyle.cs @@ -31,7 +31,8 @@ namespace Perspex.Themes.Default private Control Template(ContentControl control) { return new ContentPresenter - { + { + Id = "presenter", [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], }; } diff --git a/Perspex.Themes.Default/WindowStyle.cs b/Perspex.Themes.Default/WindowStyle.cs index 1b0f749e56..0e163d55aa 100644 --- a/Perspex.Themes.Default/WindowStyle.cs +++ b/Perspex.Themes.Default/WindowStyle.cs @@ -37,6 +37,7 @@ namespace Perspex.Themes.Default [~Border.BackgroundProperty] = control[~Window.BackgroundProperty], Content = new ContentPresenter { + Id = "presenter", [~ContentPresenter.ContentProperty] = control[~Window.ContentProperty], } };