Browse Source

Added ContentControl.ContentTemplate.

Steven Kirk 9 years ago
parent
commit
0e8995e303

+ 15 - 0
src/Avalonia.Controls/ContentControl.cs

@@ -21,6 +21,12 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<object> ContentProperty =
             AvaloniaProperty.Register<ContentControl, object>(nameof(Content));
 
+        /// <summary>
+        /// Defines the <see cref="ContentTemplate"/> property.
+        /// </summary>
+        public static readonly StyledProperty<IDataTemplate> ContentTemplateProperty =
+            AvaloniaProperty.Register<ContentControl, IDataTemplate>(nameof(ContentTemplate));
+
         /// <summary>
         /// Defines the <see cref="HorizontalContentAlignment"/> property.
         /// </summary>
@@ -51,6 +57,15 @@ namespace Avalonia.Controls
             set { SetValue(ContentProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets the data template used to display the content of the control.
+        /// </summary>
+        public IDataTemplate ContentTemplate
+        {
+            get { return GetValue(ContentTemplateProperty); }
+            set { SetValue(ContentTemplateProperty, value); }
+        }
+
         /// <summary>
         /// Gets the presenter from the control's template.
         /// </summary>

+ 6 - 0
src/Avalonia.Controls/IContentControl.cs

@@ -1,6 +1,7 @@
 // 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 Avalonia.Controls.Templates;
 using Avalonia.Layout;
 
 namespace Avalonia.Controls
@@ -16,6 +17,11 @@ namespace Avalonia.Controls
         /// </summary>
         object Content { get; set; }
 
+        /// <summary>
+        /// Gets or sets the data template used to display the content of the control.
+        /// </summary>
+        IDataTemplate ContentTemplate { get; set; }
+
         /// <summary>
         /// Gets or sets the horizontal alignment of the content within the control.
         /// </summary>

+ 16 - 1
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@@ -48,6 +48,12 @@ namespace Avalonia.Controls.Presenters
         public static readonly StyledProperty<object> ContentProperty =
             ContentControl.ContentProperty.AddOwner<ContentPresenter>();
 
+        /// <summary>
+        /// Defines the <see cref="ContentTemplate"/> property.
+        /// </summary>
+        public static readonly StyledProperty<IDataTemplate> ContentTemplateProperty =
+            ContentControl.ContentTemplateProperty.AddOwner<ContentPresenter>();
+
         /// <summary>
         /// Defines the <see cref="CornerRadius"/> property.
         /// </summary>
@@ -140,6 +146,15 @@ namespace Avalonia.Controls.Presenters
             set { SetValue(ContentProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets the data template used to display the content of the control.
+        /// </summary>
+        public IDataTemplate ContentTemplate
+        {
+            get { return GetValue(ContentTemplateProperty); }
+            set { SetValue(ContentTemplateProperty, value); }
+        }
+
         /// <summary>
         /// Gets or sets the radius of the border rounded corners.
         /// </summary>
@@ -200,7 +215,7 @@ namespace Avalonia.Controls.Presenters
         {
             var old = Child;
             var content = Content;
-            var result = this.MaterializeDataTemplate(content);
+            var result = this.MaterializeDataTemplate(content, ContentTemplate);
 
             if (old != null)
             {

+ 22 - 3
src/Avalonia.Controls/Templates/DataTemplateExtensions.cs

@@ -16,8 +16,15 @@ namespace Avalonia.Controls.Templates
         /// </summary>
         /// <param name="control">The control materializing the data template.</param>
         /// <param name="data">The data.</param>
+        /// <param name="primary">
+        /// An optional primary template that can will be tried before the
+        /// <see cref="IControl.DataTemplates"/> in the tree are searched.
+        /// </param>
         /// <returns>The data materialized as a control.</returns>
-        public static IControl MaterializeDataTemplate(this IControl control, object data)
+        public static IControl MaterializeDataTemplate(
+            this IControl control,
+            object data,
+            IDataTemplate primary = null)
         {
             if (data == null)
             {
@@ -33,7 +40,7 @@ namespace Avalonia.Controls.Templates
                 }
                 else
                 {
-                    IDataTemplate template = control.FindDataTemplate(data);
+                    IDataTemplate template = control.FindDataTemplate(data, primary);
                     IControl result;
 
                     if (template != null)
@@ -57,9 +64,21 @@ namespace Avalonia.Controls.Templates
         /// </summary>
         /// <param name="control">The control searching for the data template.</param>
         /// <param name="data">The data.</param>
+        /// <param name="primary">
+        /// An optional primary template that can will be tried before the
+        /// <see cref="IControl.DataTemplates"/> in the tree are searched.
+        /// </param>
         /// <returns>The data template or null if no matching data template was found.</returns>
-        public static IDataTemplate FindDataTemplate(this IControl control, object data)
+        public static IDataTemplate FindDataTemplate(
+            this IControl control,
+            object data,
+            IDataTemplate primary = null)
         {
+            if (primary?.Match(data) == true)
+            {
+                return primary;
+            }
+
             foreach (var i in control.GetSelfAndLogicalAncestors().OfType<IControl>())
             {
                 foreach (IDataTemplate dt in i.DataTemplates)

+ 2 - 1
src/Avalonia.Themes.Default/ContentControl.xaml

@@ -5,7 +5,8 @@
                         Background="{TemplateBinding Background}"
                         BorderBrush="{TemplateBinding BorderBrush}"
                         BorderThickness="{TemplateBinding BorderThickness}"
-                        Content="{TemplateBinding Content}" 
+                        Content="{TemplateBinding Content}"
+                        ContentTemplate="{TemplateBinding ContentTemplate}"
                         Padding="{TemplateBinding Padding}"/>
     </ControlTemplate>
   </Setter>

+ 19 - 0
tests/Avalonia.Controls.UnitTests/ContentControlTests.cs

@@ -116,6 +116,24 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(new[] { child }, target.GetLogicalChildren());
         }
 
+        [Fact]
+        public void Should_Use_ContentTemplate_To_Create_Control()
+        {
+            var target = new ContentControl
+            {
+                Template = GetTemplate(),
+                ContentTemplate = new FuncDataTemplate<string>(_ => new Canvas()),
+            };
+
+            target.Content = "Foo";
+            target.ApplyTemplate();
+            ((ContentPresenter)target.Presenter).UpdateChild();
+
+            var child = target.Presenter.Child;
+
+            Assert.IsType<Canvas>(child);
+        }
+
         [Fact]
         public void DataTemplate_Created_Control_Should_Be_Logical_Child_After_ApplyTemplate()
         {
@@ -266,6 +284,7 @@ namespace Avalonia.Controls.UnitTests
                     {
                         Name = "PART_ContentPresenter",
                         [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty],
+                        [~ContentPresenter.ContentTemplateProperty] = parent[~ContentControl.ContentTemplateProperty],
                     }
                 };
             });