diff --git a/Perspex.Controls.UnitTests/ItemsControlTests.cs b/Perspex.Controls.UnitTests/ItemsControlTests.cs index 1a0f8e838d..ec81359b88 100644 --- a/Perspex.Controls.UnitTests/ItemsControlTests.cs +++ b/Perspex.Controls.UnitTests/ItemsControlTests.cs @@ -10,11 +10,9 @@ namespace Perspex.Controls.UnitTests using System.Collections.Specialized; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; using Perspex.Collections; using Perspex.Controls; using Perspex.Controls.Presenters; - using Perspex.Layout; using Perspex.Platform; using Perspex.Styling; using Perspex.VisualTree; @@ -25,6 +23,21 @@ namespace Perspex.Controls.UnitTests [TestClass] public class ItemsControlTests { + [TestMethod] + public void Panel_Should_Have_TemplatedParent_Set_To_ItemsPresenter() + { + var target = new ItemsControl(); + + target.Template = this.GetTemplate(); + target.Items = new[] { "Foo" }; + target.ApplyTemplate(); + + var presenter = target.GetTemplateControls().OfType().Single(); + var panel = presenter.GetTemplateControls().OfType().Single(); + + Assert.AreEqual(presenter, panel.TemplatedParent); + } + [TestMethod] public void Item_Should_Have_TemplatedParent_Set_To_Null() { @@ -34,7 +47,8 @@ namespace Perspex.Controls.UnitTests target.Items = new[] { "Foo" }; target.ApplyTemplate(); - var panel = target.GetTemplateControls().OfType().Single(); + var presenter = target.GetTemplateControls().OfType().Single(); + var panel = presenter.GetTemplateControls().OfType().Single(); var item = (TextBlock)panel.GetVisualChildren().First(); Assert.IsNull(item.TemplatedParent); diff --git a/Perspex.Controls.UnitTests/TemplatedControlTests.cs b/Perspex.Controls.UnitTests/TemplatedControlTests.cs index b6ce272081..8c7eb35ed9 100644 --- a/Perspex.Controls.UnitTests/TemplatedControlTests.cs +++ b/Perspex.Controls.UnitTests/TemplatedControlTests.cs @@ -15,6 +15,7 @@ namespace Perspex.Controls.UnitTests using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Perspex.Controls; + using Perspex.Controls.Presenters; using Perspex.Controls.Primitives; using Perspex.Styling; using Perspex.VisualTree; @@ -213,6 +214,69 @@ namespace Perspex.Controls.UnitTests Assert.AreEqual(target, textBlock.TemplatedParent); } + [TestMethod] + public void Presenter_Children_Should_Not_Have_TemplatedParent_Set() + { + var target = new TestTemplatedControl + { + Template = new ControlTemplate(_ => + { + return new ContentPresenter + { + Content = new TextBlock + { + } + }; + }), + }; + + target.ApplyTemplate(); + + var presenter = target.GetTemplateControls().OfType().Single(); + var textBlock = (TextBlock)presenter.Child; + + Assert.AreEqual(target, presenter.TemplatedParent); + Assert.IsNull(textBlock.TemplatedParent); + } + + [TestMethod] + public void Nested_Templated_Controls_Have_Correct_TemplatedParent() + { + var target = new TestTemplatedControl + { + Template = new ControlTemplate(_ => + { + return new ContentControl + { + Template = new ControlTemplate(parent => + { + return new Border + { + Content = new ContentPresenter + { + [~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty), + } + }; + }), + Content = new TextBlock + { + } + }; + }), + }; + + target.ApplyTemplate(); + + var contentControl = target.GetTemplateControls().OfType().Single(); + var border = contentControl.GetTemplateControls().OfType().Single(); + var presenter = contentControl.GetTemplateControls().OfType().Single(); + var textBlock = (TextBlock)presenter.Content; + + Assert.AreEqual(target, contentControl.TemplatedParent); + Assert.AreEqual(contentControl, border.TemplatedParent); + Assert.AreEqual(contentControl, presenter.TemplatedParent); + Assert.AreEqual(target, textBlock.TemplatedParent); + } } } diff --git a/Perspex.Controls/Control.cs b/Perspex.Controls/Control.cs index 047d06ca24..ad742e7736 100644 --- a/Perspex.Controls/Control.cs +++ b/Perspex.Controls/Control.cs @@ -27,7 +27,7 @@ namespace Perspex.Controls PerspexProperty.Register("Tag"); public static readonly PerspexProperty TemplatedParentProperty = - PerspexProperty.Register("TemplatedParent", inherits: true); + PerspexProperty.Register("TemplatedParent"); public static readonly RoutedEvent RequestBringIntoViewEvent = RoutedEvent.Register("RequestBringIntoView", RoutingStrategy.Bubble); diff --git a/Perspex.Controls/ControlTemplate.cs b/Perspex.Controls/ControlTemplate.cs index 4549da6844..62852c58bd 100644 --- a/Perspex.Controls/ControlTemplate.cs +++ b/Perspex.Controls/ControlTemplate.cs @@ -7,8 +7,6 @@ namespace Perspex.Controls { using System; - using System.Linq; - using Perspex.Layout; using Perspex.Styling; public class ControlTemplate @@ -34,8 +32,7 @@ namespace Perspex.Controls { Contract.Requires(templatedParent != null); - Control root = this.build(templatedParent); - root.TemplatedParent = templatedParent; + var root = this.build(templatedParent); return root; } } diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index ab79d17bd9..bf4b16a72b 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -43,6 +43,7 @@ + diff --git a/Perspex.Controls/Presenters/ContentPresenter.cs b/Perspex.Controls/Presenters/ContentPresenter.cs index 734e61709e..be4e74a782 100644 --- a/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/Perspex.Controls/Presenters/ContentPresenter.cs @@ -13,7 +13,7 @@ namespace Perspex.Controls.Presenters using Perspex.Controls.Primitives; using Perspex.Media; - public class ContentPresenter : Control, IVisual + public class ContentPresenter : Control, IVisual, IPresenter { public static readonly PerspexProperty ContentProperty = ContentControl.ContentProperty.AddOwner(); diff --git a/Perspex.Controls/Presenters/IPresenter.cs b/Perspex.Controls/Presenters/IPresenter.cs new file mode 100644 index 0000000000..fff6bdd7f0 --- /dev/null +++ b/Perspex.Controls/Presenters/IPresenter.cs @@ -0,0 +1,24 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls.Presenters +{ + using Perspex.Controls.Primitives; + + /// + /// Interface for presenters such as and + /// . + /// + /// + /// A presenter is the gateway between a templated control and its content. When + /// a control which implements is found in the template + /// of a then that signals that the visual child + /// of the presenter is not a part of the template. + /// + public interface IPresenter : IVisual + { + } +} diff --git a/Perspex.Controls/Presenters/ItemsPresenter.cs b/Perspex.Controls/Presenters/ItemsPresenter.cs index 32e8adfbf1..e39f6e3ff7 100644 --- a/Perspex.Controls/Presenters/ItemsPresenter.cs +++ b/Perspex.Controls/Presenters/ItemsPresenter.cs @@ -11,8 +11,9 @@ namespace Perspex.Controls.Presenters using System.Collections.Specialized; using System.Reactive.Linq; using Perspex.Controls.Generators; + using Perspex.Styling; - public class ItemsPresenter : Control, IVisual + public class ItemsPresenter : Control, IVisual, IPresenter, ITemplatedControl { public static readonly PerspexProperty ItemsProperty = ItemsControl.ItemsProperty.AddOwner(); @@ -65,6 +66,7 @@ namespace Perspex.Controls.Presenters { this.ClearVisualChildren(); this.panel = this.ItemsPanel.Build(); + this.panel.TemplatedParent = this; ((IItemsPanel)this.panel).ChildLogicalParent = this.TemplatedParent as ILogical; this.AddVisualChild(this.panel); this.createdPanel = true; diff --git a/Perspex.Controls/Presenters/ScrollContentPresenter.cs b/Perspex.Controls/Presenters/ScrollContentPresenter.cs index f4fae82a1d..1b0a84b52f 100644 --- a/Perspex.Controls/Presenters/ScrollContentPresenter.cs +++ b/Perspex.Controls/Presenters/ScrollContentPresenter.cs @@ -12,7 +12,7 @@ namespace Perspex.Controls.Presenters using Perspex.Layout; using Perspex.VisualTree; - public class ScrollContentPresenter : ContentPresenter + public class ScrollContentPresenter : ContentPresenter, IPresenter { public static readonly PerspexProperty ExtentProperty = ScrollViewer.ExtentProperty.AddOwner(); diff --git a/Perspex.Controls/Presenters/TextPresenter.cs b/Perspex.Controls/Presenters/TextPresenter.cs index 5a8c5d8a76..25357db001 100644 --- a/Perspex.Controls/Presenters/TextPresenter.cs +++ b/Perspex.Controls/Presenters/TextPresenter.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace Perspex.Controls +namespace Perspex.Controls.Presenters { using System; using System.Collections.Generic; @@ -17,7 +17,7 @@ namespace Perspex.Controls using Perspex.Threading; using Perspex.VisualTree; - public class TextPresenter : TextBlock + public class TextPresenter : TextBlock, IPresenter { public static readonly PerspexProperty AcceptsReturnProperty = TextBox.AcceptsReturnProperty.AddOwner(); diff --git a/Perspex.Controls/Primitives/TemplatedControl.cs b/Perspex.Controls/Primitives/TemplatedControl.cs index 516faac6b0..56bbae3aa7 100644 --- a/Perspex.Controls/Primitives/TemplatedControl.cs +++ b/Perspex.Controls/Primitives/TemplatedControl.cs @@ -8,6 +8,7 @@ namespace Perspex.Controls.Primitives { using System; using System.Linq; + using Perspex.Controls.Presenters; using Perspex.Media; using Perspex.Styling; using Perspex.VisualTree; @@ -117,6 +118,7 @@ namespace Perspex.Controls.Primitives this.GetHashCode()); var child = this.Template.Build(this); + this.SetTemplatedParent(child); this.AddVisualChild(child); child.Parent = this; @@ -187,5 +189,18 @@ namespace Perspex.Controls.Primitives protected virtual void OnTemplateApplied() { } + + private void SetTemplatedParent(Control control) + { + control.TemplatedParent = this; + + if (!(control is IPresenter)) + { + foreach (var child in control.GetVisualChildren().OfType()) + { + this.SetTemplatedParent(child); + } + } + } } } diff --git a/Perspex.Themes.Default/TabStripStyle.cs b/Perspex.Themes.Default/TabStripStyle.cs index eed4683886..e6e3fbcddb 100644 --- a/Perspex.Themes.Default/TabStripStyle.cs +++ b/Perspex.Themes.Default/TabStripStyle.cs @@ -25,7 +25,7 @@ namespace Perspex.Themes.Default new Setter(TabStrip.TemplateProperty, ControlTemplate.Create(this.Template)), }, }, - new Style(x => x.OfType().Template().OfType()) + new Style(x => x.OfType().Template().OfType().Template().OfType()) { Setters = new[] { diff --git a/Perspex.Themes.Default/TextBoxStyle.cs b/Perspex.Themes.Default/TextBoxStyle.cs index 256406a59d..bc0f045137 100644 --- a/Perspex.Themes.Default/TextBoxStyle.cs +++ b/Perspex.Themes.Default/TextBoxStyle.cs @@ -12,6 +12,7 @@ namespace Perspex.Themes.Default using Perspex.Media; using Perspex.Controls.Shapes; using Perspex.Styling; + using Perspex.Controls.Presenters; public class TextBoxStyle : Styles {