Browse Source

Fix TemplateBinding in custom IControlTemplate implementations (#17427)

* Add failing test for TemplateBinding inside custom control template

* Fix TemplateBinding XAML compilation error for custom IControlTemplate

---------

Co-authored-by: Max Katz <maxkatz6@outlook.com>
pull/17442/head
Julien Lebosquain 2 years ago
committed by GitHub
parent
commit
8f7686fdfa
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs
  2. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  3. 53
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs

@ -11,7 +11,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{ {
if (!(node is XamlAstObjectNode on if (!(node is XamlAstObjectNode on
&& on.Type.GetClrType() == context.GetAvaloniaTypes().ControlTemplate)) && context.GetAvaloniaTypes().IControlTemplate.IsAssignableFrom(on.Type.GetClrType())))
return node; return node;
var tt = on.Children.OfType<XamlAstXamlPropertyValueNode>().FirstOrDefault(ch => var tt = on.Children.OfType<XamlAstXamlPropertyValueNode>().FirstOrDefault(ch =>
ch.Property.GetClrProperty().Name == "TargetType"); ch.Property.GetClrProperty().Name == "TargetType");

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

@ -129,6 +129,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType WindowTransparencyLevel { get; } public IXamlType WindowTransparencyLevel { get; }
public IXamlType IReadOnlyListOfT { get; } public IXamlType IReadOnlyListOfT { get; }
public IXamlType ControlTemplate { get; } public IXamlType ControlTemplate { get; }
public IXamlType IControlTemplate { get; }
public IXamlType EventHandlerT { get; } public IXamlType EventHandlerT { get; }
public IXamlMethod GetClassProperty { get; } public IXamlMethod GetClassProperty { get; }
@ -325,6 +326,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style"); Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style");
ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme"); ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme");
ControlTemplate = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Templates.ControlTemplate"); ControlTemplate = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Templates.ControlTemplate");
IControlTemplate = cfg.TypeSystem.GetType("Avalonia.Controls.Templates.IControlTemplate");
IReadOnlyListOfT = cfg.TypeSystem.GetType("System.Collections.Generic.IReadOnlyList`1"); IReadOnlyListOfT = cfg.TypeSystem.GetType("System.Collections.Generic.IReadOnlyList`1");
EventHandlerT = cfg.TypeSystem.GetType("System.EventHandler`1"); EventHandlerT = cfg.TypeSystem.GetType("System.EventHandler`1");
Interactivity = new InteractivityWellKnownTypes(cfg); Interactivity = new InteractivityWellKnownTypes(cfg);

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

@ -6,10 +6,12 @@ using Avalonia.Controls;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Markup.Xaml.Templates; using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Xunit; using Xunit;
@ -437,13 +439,47 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(RuntimeXamlDiagnosticSeverity.Info, warning.Severity); Assert.Equal(RuntimeXamlDiagnosticSeverity.Info, warning.Severity);
Assert.Contains("'PART_MainContentBorder'", warning.Title); Assert.Contains("'PART_MainContentBorder'", warning.Title);
} }
#nullable enable
[Fact]
public void Custom_ControlTemplate_Allows_TemplateBindings()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = (Window)AvaloniaRuntimeXamlLoader.Load(
"""
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Avalonia.Markup.Xaml.UnitTests.Xaml">
<Button Content="Foo">
<Button.Template>
<controls:CustomControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"/>
</controls:CustomControlTemplate>
</Button.Template>
</Button>
</Window>
""");
var button = Assert.IsType<Button>(window.Content);
window.ApplyTemplate();
button.ApplyTemplate();
var presenter = button.Presenter;
Assert.NotNull(presenter);
Assert.Equal("Foo", presenter.Content);
}
}
} }
public class ListBoxHierarchyLine : Panel public class ListBoxHierarchyLine : Panel
{ {
public static readonly StyledProperty<DashStyle> LineDashStyleProperty = public static readonly StyledProperty<DashStyle?> LineDashStyleProperty =
AvaloniaProperty.Register<ListBoxHierarchyLine, DashStyle>(nameof(LineDashStyle)); AvaloniaProperty.Register<ListBoxHierarchyLine, DashStyle?>(nameof(LineDashStyle));
public DashStyle LineDashStyle public DashStyle? LineDashStyle
{ {
get => GetValue(LineDashStyleProperty); get => GetValue(LineDashStyleProperty);
set => SetValue(LineDashStyleProperty, value); set => SetValue(LineDashStyleProperty, value);
@ -459,4 +495,15 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
public class CustomButtonWithParts : CustomControlWithParts public class CustomButtonWithParts : CustomControlWithParts
{ {
} }
public class CustomControlTemplate : IControlTemplate
{
[Content]
[TemplateContent]
public object? Content { get; set; }
public Type? TargetType { get; set; }
public TemplateResult<Control>? Build(TemplatedControl control) => TemplateContent.Load(Content);
}
} }

Loading…
Cancel
Save