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