committed by
GitHub
13 changed files with 439 additions and 73 deletions
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.Runtime |
|||
{ |
|||
public interface IAvaloniaXamlIlControlTemplateProvider |
|||
{ |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue