Browse Source

Merge pull request #7679 from AvaloniaUI/refactor/controltemplate-binding-priority

Don't use LocalValue priority for values in ControlTemplate
pull/8580/head
Max Katz 4 years ago
committed by GitHub
parent
commit
8f7ed7de1a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  2. 3
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  3. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  4. 58
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs
  5. 8
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  6. 74
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  7. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  8. 4
      src/Markup/Avalonia.Markup.Xaml/Extensions.cs
  9. 8
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  10. 8
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlControlTemplateProvider.cs
  11. 5
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  12. 66
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  13. 273
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

3
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -95,6 +95,9 @@
<Compile Include="../Avalonia.Controls/GridLength.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Data/BindingPriority.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/RelativePoint.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>

3
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -251,7 +251,8 @@ namespace Avalonia.Build.Tasks
var populateBuilder = classTypeDefinition == null ?
builder :
typeSystem.CreateTypeBuilder(classTypeDefinition);
compiler.Compile(parsed, contextClass,
compiler.Compile(parsed,
contextClass,
compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
classTypeDefinition == null),
buildName == null ? null : compiler.DefineBuildMethod(builder, parsed, buildName, true),

1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -68,6 +68,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlCompiledBindingsMetadataRemover()
);
Transformers.Add(new AvaloniaXamlIlControlTemplatePriorityTransformer());
Transformers.Add(new AvaloniaXamlIlMetadataRemover());
Transformers.Add(new AvaloniaXamlIlRootObjectScope());

58
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs

@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Data;
using XamlX.Ast;
using XamlX.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
/// <summary>
/// Transforms property assignments within ControlTemplates to use Style priority where possible.
/// </summary>
class AvaloniaXamlIlControlTemplatePriorityTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
var bindingPriorityType = context.GetAvaloniaTypes().BindingPriority;
// The node is a candidate for transformation if:
// - It's a property assignment to an Avalonia property
// - There's a ControlTemplate ancestor
// - The property has a single value
if (node is XamlPropertyAssignmentNode prop &&
prop.Property is XamlIlAvaloniaProperty avaloniaProperty &&
context.ParentNodes().Any(IsControlTemplate) &&
prop.Values.Count == 1)
{
var priorityValueSetters = new List<IXamlPropertySetter>();
// Iterate through the possible setters, trying to find a setter on the property
// which has a BindingPriority parameter followed by the parameter of the existing
// setter.
foreach (var setter in prop.PossibleSetters)
{
var s = avaloniaProperty.Setters.FirstOrDefault(x =>
x.Parameters[0] == bindingPriorityType &&
x.Parameters[1] == setter.Parameters[0]);
if (s != null)
priorityValueSetters.Add(s);
}
// If any BindingPriority setters were found, use those.
if (priorityValueSetters.Count > 0)
{
prop.PossibleSetters = priorityValueSetters;
prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.TemplatedParent));
}
}
return node;
}
private static bool IsControlTemplate(IXamlAstNode node)
{
return node is AvaloniaXamlIlTargetTypeMetadataNode tt &&
tt.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate;
}
}
}

8
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -15,6 +15,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType AvaloniaObjectExtensions { get; }
public IXamlType AvaloniaProperty { get; }
public IXamlType AvaloniaPropertyT { get; }
public IXamlType StyledPropertyT { get; }
public IXamlMethod AvaloniaObjectSetStyledPropertyValue { get; }
public IXamlType AvaloniaAttachedPropertyT { get; }
public IXamlType IBinding { get; }
public IXamlMethod AvaloniaObjectBindMethod { get; }
@ -105,8 +107,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
AvaloniaObjectExtensions = cfg.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions");
AvaloniaProperty = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty");
AvaloniaPropertyT = cfg.TypeSystem.GetType("Avalonia.AvaloniaProperty`1");
StyledPropertyT = cfg.TypeSystem.GetType("Avalonia.StyledProperty`1");
AvaloniaAttachedPropertyT = cfg.TypeSystem.GetType("Avalonia.AttachedProperty`1");
BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority");
AvaloniaObjectSetStyledPropertyValue = AvaloniaObject
.FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue"
&& m.Parameters.Count == 3
&& m.Parameters[0].Name == "StyledPropertyBase`1"
&& m.Parameters[2].Equals(BindingPriority));
IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
IDisposable = cfg.TypeSystem.GetType("System.IDisposable");
ICommand = cfg.TypeSystem.GetType("System.Windows.Input.ICommand");

74
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

@ -185,11 +185,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
AvaloniaXamlIlWellKnownTypes types)
:base(original, original.Name, original.DeclaringType, original.Getter, original.Setters)
{
var assignBinding = original.CustomAttributes.Any(ca => ca.Type.Equals(types.AssignBindingAttribute));
AvaloniaProperty = field;
CustomAttributes = original.CustomAttributes;
if (!original.CustomAttributes.Any(ca => ca.Type.Equals(types.AssignBindingAttribute)))
if (!assignBinding)
Setters.Insert(0, new BindingSetter(types, original.DeclaringType, field));
// Styled and attached properties can be set with a BindingPriority when they're
// assigned in a ControlTemplate.
if (field.FieldType.GenericTypeDefinition == types.StyledPropertyT ||
field.FieldType.GenericTypeDefinition == types.AvaloniaAttachedPropertyT)
{
var propertyType = field.FieldType.GenericArguments[0];
Setters.Insert(0, new SetValueWithPrioritySetter(types, original.DeclaringType, field, propertyType));
if (!assignBinding)
Setters.Insert(1, new BindingWithPrioritySetter(types, original.DeclaringType, field));
}
Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field));
}
@ -240,6 +253,63 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
class BindingWithPrioritySetter : AvaloniaPropertyCustomSetter
{
public BindingWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types,
IXamlType declaringType,
IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty)
{
Parameters = new[] { types.BindingPriority, types.IBinding };
}
public override void Emit(IXamlILEmitter emitter)
{
using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding))
using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int))
emitter
.Stloc(bloc.Local)
.Stloc(priorityLocal.Local)
.Ldsfld(AvaloniaProperty)
.Ldloc(bloc.Local)
// TODO: provide anchor?
.Ldnull();
emitter.EmitCall(Types.AvaloniaObjectBindMethod, true);
}
}
class SetValueWithPrioritySetter : AvaloniaPropertyCustomSetter
{
public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty,
IXamlType propertyType)
: base(types, declaringType, avaloniaProperty)
{
Parameters = new[] { types.BindingPriority, propertyType };
}
public override void Emit(IXamlILEmitter emitter)
{
/*
Current stack:
- object
- binding priority
- value
*/
var method = Types.AvaloniaObjectSetStyledPropertyValue
.MakeGenericMethod(new[] { Parameters[1] });
using (var valueLocal = emitter.LocalsPool.GetLocal(Parameters[1]))
using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int))
emitter
.Stloc(valueLocal.Local)
.Stloc(priorityLocal.Local)
.Ldsfld(AvaloniaProperty)
.Ldloc(valueLocal.Local)
.Ldloc(priorityLocal.Local)
.EmitCall(method, true);
}
}
class UnsetValueSetter : AvaloniaPropertyCustomSetter
{
public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty)

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -48,6 +48,7 @@
<Compile Include="Templates\Template.cs" />
<Compile Include="Templates\TemplateContent.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlControlTemplateProvider.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlParentStackProvider.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
<Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />

4
src/Markup/Avalonia.Markup.Xaml/Extensions.cs

@ -24,10 +24,10 @@ namespace Avalonia.Markup.Xaml
public static IEnumerable<T> GetParents<T>(this IServiceProvider sp)
{
return sp.GetService<IAvaloniaXamlIlParentStackProvider>().Parents.OfType<T>();
}
public static bool IsInControlTemplate(this IServiceProvider sp) => sp.GetService<IAvaloniaXamlIlControlTemplateProvider>() != null;
public static Type ResolveType(this IServiceProvider ctx, string namespacePrefix, string type)
{
var tr = ctx.GetService<IXamlTypeResolver>();

8
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -11,6 +11,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public class DynamicResourceExtension : IBinding
{
private object? _anchor;
private BindingPriority _priority;
public DynamicResourceExtension()
{
@ -25,6 +26,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public IBinding ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider.IsInControlTemplate())
_priority = BindingPriority.TemplatedParent;
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
if (!(provideTarget.TargetObject is IStyledElement))
@ -53,12 +57,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
if (control != null)
{
var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty));
return InstancedBinding.OneWay(source);
return InstancedBinding.OneWay(source, _priority);
}
else if (_anchor is IResourceProvider resourceProvider)
{
var source = resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty));
return InstancedBinding.OneWay(source);
return InstancedBinding.OneWay(source, _priority);
}
return null;

8
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlControlTemplateProvider.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Avalonia.Markup.Xaml.XamlIl.Runtime
{
public interface IAvaloniaXamlIlControlTemplateProvider
{
}
}

5
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@ -42,7 +42,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
class DeferredParentServiceProvider :
IAvaloniaXamlIlParentStackProvider,
IServiceProvider,
IRootObjectProvider
IRootObjectProvider,
IAvaloniaXamlIlControlTemplateProvider
{
private readonly IServiceProvider _parentProvider;
private readonly List<IResourceNode> _parentResourceNodes;
@ -75,6 +76,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
return this;
if (serviceType == typeof(IRootObjectProvider))
return this;
if (serviceType == typeof(IAvaloniaXamlIlControlTemplateProvider))
return this;
return _parentProvider?.GetService(serviceType);
}

66
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -283,70 +283,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(expected4, grid.RowDefinitions[3].Height);
}
[Fact]
public void ControlTemplate_With_Nested_Child_Is_Operational()
{
var xaml = @"
<ControlTemplate xmlns='https://github.com/avaloniaui'>
<ContentControl Name='parent'>
<ContentControl Name='child' />
</ContentControl>
</ControlTemplate>
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var parent = (ContentControl)template.Build(new ContentControl()).Control;
Assert.Equal("parent", parent.Name);
var child = parent.Content as ContentControl;
Assert.NotNull(child);
Assert.Equal("child", child.Name);
}
[Fact]
public void ControlTemplate_With_TargetType_Is_Operational()
{
var xaml = @"
<ControlTemplate xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
TargetType='{x:Type ContentControl}'>
<ContentPresenter Content='{TemplateBinding Content}' />
</ControlTemplate>
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
Assert.Equal(typeof(ContentControl), template.TargetType);
Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Control);
}
[Fact]
public void ControlTemplate_With_Panel_Children_Are_Added()
{
var xaml = @"
<ControlTemplate xmlns='https://github.com/avaloniaui'>
<Panel Name='panel'>
<ContentControl Name='Foo' />
<ContentControl Name='Bar' />
</Panel>
</ControlTemplate>
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var panel = (Panel)template.Build(new ContentControl()).Control;
Assert.Equal(2, panel.Children.Count);
var foo = panel.Children[0];
var bar = panel.Children[1];
Assert.Equal("Foo", foo.Name);
Assert.Equal("Bar", bar.Name);
}
[Fact]
public void Named_x_Control_Is_Added_To_NameScope_Simple()
{
@ -363,7 +299,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
}
[Fact]
public void Standart_TypeConverter_Is_Used()
public void Standard_TypeConverter_Is_Used()
{
var xaml = @"<UserControl xmlns='https://github.com/avaloniaui' Width='200.5' />";

273
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

@ -0,0 +1,273 @@
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
public class ControlTemplateTests : XamlTestBase
{
[Fact]
public void Inline_ControlTemplate_Styled_Values_Are_Set_With_Style_Priority()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Button>
<Button.Template>
<ControlTemplate>
<ContentPresenter Name='PART_ContentPresenter'
Background='Red'/>
</ControlTemplate>
</Button.Template>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = (Button)window.Content;
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = (ContentPresenter)button.Presenter;
Assert.Equal(Brushes.Red, presenter.Background);
var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty);
Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority);
}
}
[Fact]
public void Style_ControlTemplate_Styled_Values_Are_Set_With_Style_Priority()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Styles>
<Style Selector='Button'>
<Setter Property='Template'>
<ControlTemplate>
<ContentPresenter Name='PART_ContentPresenter'
Background='Red'/>
</ControlTemplate>
</Setter>
</Style>
</Window.Styles>
<Button/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = (Button)window.Content;
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = (ContentPresenter)button.Presenter;
Assert.Equal(Brushes.Red, presenter.Background);
var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty);
Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority);
}
}
[Fact]
public void ControlTemplate_Attached_Values_Are_Set_With_Style_Priority()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Button>
<Button.Template>
<ControlTemplate>
<ContentPresenter Name='PART_ContentPresenter'
DockPanel.Dock='Top'/>
</ControlTemplate>
</Button.Template>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = (Button)window.Content;
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = (ContentPresenter)button.Presenter;
Assert.Equal(Dock.Top, DockPanel.GetDock(presenter));
var diagnostic = presenter.GetDiagnostic(DockPanel.DockProperty);
Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority);
}
}
[Fact]
public void ControlTemplate_StaticResources_Are_Set_With_Style_Priority()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<SolidColorBrush x:Key='red'>Red</SolidColorBrush>
</Window.Resources>
<Button Content='Foo'>
<Button.Template>
<ControlTemplate>
<ContentPresenter Name='PART_ContentPresenter'
Background='{StaticResource red}'/>
</ControlTemplate>
</Button.Template>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = (Button)window.Content;
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = (ContentPresenter)button.Presenter;
Assert.Equal(Brushes.Red, presenter.Background);
var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty);
Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority);
}
}
[Fact]
public void ControlTemplate_DynamicResources_Are_Set_With_Style_Priority()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<SolidColorBrush x:Key='red'>Red</SolidColorBrush>
</Window.Resources>
<Button Content='Foo'>
<Button.Template>
<ControlTemplate>
<ContentPresenter Name='PART_ContentPresenter'
Background='{DynamicResource red}'/>
</ControlTemplate>
</Button.Template>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = (Button)window.Content;
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = (ContentPresenter)button.Presenter;
Assert.Equal(Brushes.Red, presenter.Background);
var diagnostic = presenter.GetDiagnostic(Button.BackgroundProperty);
Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority);
}
}
[Fact]
public void ControlTemplate_TemplateBindings_Are_Set_With_TemplatedParent_Priority()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Button Content='Foo'>
<Button.Template>
<ControlTemplate>
<ContentPresenter Name='PART_ContentPresenter'
Content='{TemplateBinding Content}'/>
</ControlTemplate>
</Button.Template>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = (Button)window.Content;
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = (ContentPresenter)button.Presenter;
Assert.Equal("Foo", presenter.Content);
var diagnostic = presenter.GetDiagnostic(ContentPresenter.ContentProperty);
Assert.Equal(BindingPriority.TemplatedParent, diagnostic.Priority);
}
}
[Fact]
public void ControlTemplate_With_Nested_Child_Is_Operational()
{
var xaml = @"
<ControlTemplate xmlns='https://github.com/avaloniaui'>
<ContentControl Name='parent'>
<ContentControl Name='child' />
</ContentControl>
</ControlTemplate>
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var parent = (ContentControl)template.Build(new ContentControl()).Control;
Assert.Equal("parent", parent.Name);
var child = parent.Content as ContentControl;
Assert.NotNull(child);
Assert.Equal("child", child.Name);
}
[Fact]
public void ControlTemplate_With_TargetType_Is_Operational()
{
var xaml = @"
<ControlTemplate xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
TargetType='{x:Type ContentControl}'>
<ContentPresenter Content='{TemplateBinding Content}' />
</ControlTemplate>
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
Assert.Equal(typeof(ContentControl), template.TargetType);
Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Control);
}
[Fact]
public void ControlTemplate_With_Panel_Children_Are_Added()
{
var xaml = @"
<ControlTemplate xmlns='https://github.com/avaloniaui'>
<Panel Name='panel'>
<ContentControl Name='Foo' />
<ContentControl Name='Bar' />
</Panel>
</ControlTemplate>
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var panel = (Panel)template.Build(new ContentControl()).Control;
Assert.Equal(2, panel.Children.Count);
var foo = panel.Children[0];
var bar = panel.Children[1];
Assert.Equal("Foo", foo.Name);
Assert.Equal("Bar", bar.Name);
}
}
}
Loading…
Cancel
Save