Skip to content

Commit

Permalink
Fix TemplateBinding in custom IControlTemplate implementations (#17427)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
MrJul and maxkatz6 committed Nov 14, 2024
1 parent 612bafb commit e6fda91
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer : IXamlAstTrans
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!(node is XamlAstObjectNode on
&& on.Type.GetClrType() == context.GetAvaloniaTypes().ControlTemplate))
&& context.GetAvaloniaTypes().IControlTemplate.IsAssignableFrom(on.Type.GetClrType())))
return node;
var tt = on.Children.OfType<XamlAstXamlPropertyValueNode>().FirstOrDefault(ch =>
ch.Property.GetClrProperty().Name == "TargetType");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ sealed class AvaloniaXamlIlWellKnownTypes
public IXamlType WindowTransparencyLevel { get; }
public IXamlType IReadOnlyListOfT { get; }
public IXamlType ControlTemplate { get; }
public IXamlType IControlTemplate { get; }
public IXamlType EventHandlerT { get; }

sealed internal class InteractivityWellKnownTypes
Expand Down Expand Up @@ -324,6 +325,7 @@ public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style");
ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme");
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");
EventHandlerT = cfg.TypeSystem.GetType("System.EventHandler`1");
Interactivity = new InteractivityWellKnownTypes(cfg);
Expand Down
53 changes: 50 additions & 3 deletions tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Xunit;
Expand Down Expand Up @@ -437,13 +439,47 @@ public void ControlTemplate_Outputs_Error_When_Missing_TemplatePart_Nested_ItemT
Assert.Equal(RuntimeXamlDiagnosticSeverity.Info, warning.Severity);
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 static readonly StyledProperty<DashStyle> LineDashStyleProperty =
AvaloniaProperty.Register<ListBoxHierarchyLine, DashStyle>(nameof(LineDashStyle));
public static readonly StyledProperty<DashStyle?> LineDashStyleProperty =
AvaloniaProperty.Register<ListBoxHierarchyLine, DashStyle?>(nameof(LineDashStyle));

public DashStyle LineDashStyle
public DashStyle? LineDashStyle
{
get => GetValue(LineDashStyleProperty);
set => SetValue(LineDashStyleProperty, value);
Expand All @@ -459,4 +495,15 @@ public class CustomControlWithParts : ContentControl
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);
}
}

0 comments on commit e6fda91

Please sign in to comment.