Browse Source

Added <Template>

The new <Template> is a template that can be used as a setter value,
meaning that the setter will materialize the template each time it sets
the value. Make assigning a control to `Setter.Value` throw an error
indicating that the control should be wrapped in a template.
pull/569/head
Steven Kirk 10 years ago
parent
commit
d772017768
  1. 3
      src/Avalonia.Controls/Templates/FuncTemplate`1.cs
  2. 6
      src/Avalonia.Controls/Templates/ITemplate`1.cs
  3. 3
      src/Avalonia.Styling/Avalonia.Styling.csproj
  4. 10
      src/Avalonia.Styling/Styling/ITemplate.cs
  5. 34
      src/Avalonia.Styling/Styling/Setter.cs
  6. 10
      src/Markup/Avalonia.Markup.Xaml/Templates/FocusAdornerTemplate.cs
  7. 10
      src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  8. 11
      src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
  9. 23
      tests/Avalonia.Styling.UnitTests/SetterTests.cs

3
src/Avalonia.Controls/Templates/FuncTemplate`1.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Styling;
namespace Avalonia.Controls.Templates
{
@ -34,5 +35,7 @@ namespace Avalonia.Controls.Templates
{
return _func();
}
object ITemplate.Build() => Build();
}
}

6
src/Avalonia.Controls/Templates/ITemplate`1.cs

@ -1,13 +1,15 @@
// 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.Styling;
namespace Avalonia.Controls
{
/// <summary>
/// Creates a control.
/// </summary>
/// <typeparam name="TControl">The type of control.</typeparam>
public interface ITemplate<TControl> where TControl : IControl
public interface ITemplate<TControl> : ITemplate where TControl : IControl
{
/// <summary>
/// Creates the control.
@ -15,6 +17,6 @@ namespace Avalonia.Controls
/// <returns>
/// The created control.
/// </returns>
TControl Build();
new TControl Build();
}
}

3
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@ -49,6 +49,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Styling\ActivatedSubject.cs" />
<Compile Include="Styling\ActivatedValue.cs" />
<Compile Include="Styling\ITemplate.cs" />
<Compile Include="Styling\TemplateSelector.cs" />
<Compile Include="Styling\DescendentSelector.cs" />
<Compile Include="Styling\ChildSelector.cs" />

10
src/Avalonia.Styling/Styling/ITemplate.cs

@ -0,0 +1,10 @@
// 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.
namespace Avalonia.Styling
{
public interface ITemplate
{
object Build();
}
}

34
src/Avalonia.Styling/Styling/Setter.cs

@ -19,6 +19,8 @@ namespace Avalonia.Styling
/// </remarks>
public class Setter : ISetter
{
private object _value;
/// <summary>
/// Initializes a new instance of the <see cref="Setter"/> class.
/// </summary>
@ -54,8 +56,22 @@ namespace Avalonia.Styling
[DependsOn(nameof(Property))]
public object Value
{
get;
set;
get
{
return _value;
}
set
{
if (value is IStyleable)
{
throw new ArgumentException(
"Cannot assign a control to Style.Value. Wrap the control in a <Template>.",
"value");
}
_value = value;
}
}
/// <summary>
@ -75,17 +91,25 @@ namespace Avalonia.Styling
throw new InvalidOperationException("Setter.Property must be set.");
}
var binding = Value as IBinding;
var value = Value;
var binding = value as IBinding;
if (binding == null)
{
var template = value as ITemplate;
if (template != null)
{
value = template.Build();
}
if (activator == null)
{
return control.Bind(Property, ObservableEx.SingleValue(Value), BindingPriority.Style);
return control.Bind(Property, ObservableEx.SingleValue(value), BindingPriority.Style);
}
else
{
var activated = new ActivatedValue(activator, Value, description);
var activated = new ActivatedValue(activator, value, description);
return control.Bind(Property, activated, BindingPriority.StyleTrigger);
}
}

10
src/Markup/Avalonia.Markup.Xaml/Templates/FocusAdornerTemplate.cs

@ -3,17 +3,11 @@
using Avalonia.Controls;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.Templates
{
public class FocusAdornerTemplate : ITemplate<IControl>
public class FocusAdornerTemplate : Template
{
[Content]
public TemplateContent Content { get; set; }
public IControl Build()
{
return Content.Load();
}
}
}

10
src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 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;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.Templates
{
@ -18,5 +16,7 @@ namespace Avalonia.Markup.Xaml.Templates
{
return (IPanel)Content.Load();
}
object ITemplate.Build() => Build();
}
}

11
src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs

@ -1,10 +1,19 @@
// 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;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.Templates
{
public class Template
public class Template : ITemplate<IControl>
{
[Content]
public TemplateContent Content { get; set; }
public IControl Build() => Content.Load();
object ITemplate.Build() => Build();
}
}

23
tests/Avalonia.Styling.UnitTests/SetterTests.cs

@ -6,11 +6,21 @@ using Moq;
using Avalonia.Controls;
using Avalonia.Data;
using Xunit;
using System;
using Avalonia.Controls.Templates;
namespace Avalonia.Styling.UnitTests
{
public class SetterTests
{
[Fact]
public void Cannot_Assign_Control_To_Value()
{
var target = new Setter();
Assert.Throws<ArgumentException>(() => target.Value = new Border());
}
[Fact]
public void Setter_Should_Apply_Binding_To_Property()
{
@ -25,5 +35,18 @@ namespace Avalonia.Styling.UnitTests
Assert.Equal("foo", control.Text);
}
[Fact]
public void Setter_Should_Materialize_Template_To_Property()
{
var control = new Decorator();
var template = new FuncTemplate<Canvas>(() => new Canvas());
var style = Mock.Of<IStyle>();
var setter = new Setter(Decorator.ChildProperty, template);
setter.Apply(style, control, null);
Assert.IsType<Canvas>(control.Child);
}
}
}

Loading…
Cancel
Save