Browse Source

Added IContentPresenterHost.

Similarly to how we now have IItemsPresenterHost.
Steven Kirk 10 years ago
parent
commit
4845dfd0fa

+ 4 - 9
src/Perspex.Controls/ContentControl.cs

@@ -16,7 +16,7 @@ namespace Perspex.Controls
     /// <summary>
     /// Displays <see cref="Content"/> according to a <see cref="FuncDataTemplate"/>.
     /// </summary>
-    public class ContentControl : TemplatedControl, IContentControl
+    public class ContentControl : TemplatedControl, IContentControl, IContentPresenterHost
     {
         /// <summary>
         /// Defines the <see cref="Content"/> property.
@@ -57,7 +57,7 @@ namespace Perspex.Controls
         /// <summary>
         /// Gets the presenter from the control's template.
         /// </summary>
-        public ContentPresenter Presenter
+        public IContentPresenter Presenter
         {
             get;
             private set;
@@ -82,14 +82,9 @@ namespace Perspex.Controls
         }
 
         /// <inheritdoc/>
-        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<ContentPresenter>("PART_ContentPresenter");
+            Presenter = presenter;
         }
     }
 }

+ 1 - 0
src/Perspex.Controls/Perspex.Controls.csproj

@@ -57,6 +57,7 @@
     <Compile Include="Platform\ITopLevelRenderer.cs" />
     <Compile Include="Platform\IWindowingPlatform.cs" />
     <Compile Include="Platform\PlatformManager.cs" />
+    <Compile Include="Presenters\IContentPresenterHost.cs" />
     <Compile Include="Presenters\IItemsPresenterHost.cs" />
     <Compile Include="Primitives\HeaderedSelectingControl.cs" />
     <Compile Include="Primitives\IScrollable.cs" />

+ 6 - 0
src/Perspex.Controls/Presenters/ContentPresenter.cs

@@ -34,6 +34,7 @@ namespace Perspex.Controls.Presenters
         static ContentPresenter()
         {
             ContentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged);
+            TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged);
         }
 
         /// <summary>
@@ -146,5 +147,10 @@ namespace Perspex.Controls.Presenters
             _createdChild = false;
             InvalidateMeasure();
         }
+
+        private void TemplatedParentChanged(PerspexPropertyChangedEventArgs e)
+        {
+            (e.NewValue as IContentPresenterHost)?.RegisterContentPresenter(this);
+        }
     }
 }

+ 27 - 0
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
+{
+    /// <summary>
+    /// Represents a control which hosts a content presenter.
+    /// </summary>
+    /// <remarks>
+    /// This interface is implemented by <see cref="ContentControl"/> which usually contains a
+    /// <see cref="ContentPresenter"/> and exposes it through its 
+    /// <see cref="ContentControl.Presenter"/> 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.
+    /// </remarks>
+    public interface IContentPresenterHost : ITemplatedControl
+    {
+        /// <summary>
+        /// Registers an <see cref="IContentPresenter"/> with a host control.
+        /// </summary>
+        /// <param name="presenter">The content presenter.</param>
+        void RegisterContentPresenter(IContentPresenter presenter);
+    }
+}

+ 13 - 13
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<Border>(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<ContentPresenter>();
             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);
         }

+ 2 - 2
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

+ 2 - 2
tests/Perspex.Controls.UnitTests/ListBoxTests.cs

@@ -42,7 +42,7 @@ namespace Perspex.Controls.UnitTests
             var text = target.Presenter.Panel.Children
                 .OfType<ListBoxItem>()
                 .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<TextBlock>()
                 .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();

+ 1 - 1
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();

+ 12 - 0
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<IContentPresenterHost>();
+            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()
         {

+ 1 - 1
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<TextBlock>(target.Presenter.Child);
         }