diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index f325e6e2d6..4ece433530 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; + using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; using XamlX; using XamlX.Ast; @@ -51,6 +52,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(), new AvaloniaXamlIlBindingPathParser(), new AvaloniaXamlIlPropertyPathTransformer(), + new AvaloniaXamlIlSetterTargetTypeMetadataTransformer(), new AvaloniaXamlIlSetterTransformer(), new AvaloniaXamlIlConstructorServiceProviderTransformer(), new AvaloniaXamlIlTransitionsTypeMetadataTransformer(), diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs new file mode 100644 index 0000000000..ebc6c01ba8 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs @@ -0,0 +1,34 @@ +using System.Linq; +using XamlX; +using XamlX.Ast; +using XamlX.Transform; +using XamlX.Transform.Transformers; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; + +internal class AvaloniaXamlIlSetterTargetTypeMetadataTransformer : IXamlAstTransformer +{ + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (node is XamlAstObjectNode on + && on.Children.FirstOrDefault(c => c is XamlAstXmlDirective + { + Namespace: XamlNamespaces.Xaml2006, + Name: "SetterTargetType" + }) is { } typeDirective) + { + var value = ((XamlAstXmlDirective)typeDirective).Values.Single(); + var type = value is XamlTypeExtensionNode typeNode ? typeNode.Value + : value is XamlAstTextNode tn ? TypeReferenceResolver.ResolveType(context, tn.Text, false, tn, true) + : null; + on.Children.Remove(typeDirective); + + if (type is null) + { + throw new XamlParseException("Unable to resolve SetterTargetType type", typeDirective); + } + return new AvaloniaXamlIlTargetTypeMetadataNode(on, type, AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style); + } + return node; + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index ceaec972f6..5a6fd8246d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using XamlX; using XamlX.Ast; using XamlX.Emit; using XamlX.IL; @@ -8,7 +9,6 @@ using XamlX.TypeSystem; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { - using XamlParseException = XamlX.XamlParseException; class AvaloniaXamlIlSetterTransformer : IXamlAstTransformer { public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) @@ -17,10 +17,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers && on.Type.GetClrType().FullName == "Avalonia.Styling.Setter")) return node; - var targetTypeNode = context.ParentNodes() + IXamlType targetType = null; + IXamlLineInfo lineInfo = null; + + var styleParent = context.ParentNodes() .OfType() - .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style) ?? - throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType", node); + .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style); + + if (styleParent != null) + { + targetType = styleParent.TargetType.GetClrType() + ?? throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType. If setter is not part of the style, you can set x:SetterTargetType directive on its parent.", node); + lineInfo = on; + } + + if (targetType == null) + { + throw new XamlParseException("Could not determine target type of Setter", node); + } IXamlType propType = null; var property = @on.Children.OfType() @@ -31,9 +45,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (propertyName == null) throw new XamlParseException("Setter.Property must be a string", node); - var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName, - new XamlAstClrTypeReference(targetTypeNode, targetTypeNode.TargetType.GetClrType(), false), property.Values[0]); + new XamlAstClrTypeReference(lineInfo, targetType, false), property.Values[0]); property.Values = new List {avaloniaPropertyNode}; propType = avaloniaPropertyNode.AvaloniaPropertyType; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/SetterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/SetterTests.cs new file mode 100644 index 0000000000..6fc0f2d91c --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/SetterTests.cs @@ -0,0 +1,51 @@ +using Avalonia.Controls; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests; + +public class SetterTests : XamlTestBase +{ + [Fact] + public void SetterTargetType_Should_Understand_xType_Extensions() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var animation = (Animation.Animation)AvaloniaRuntimeXamlLoader.Load(xaml); + var setter = (Setter)animation.Children[0].Setters[0]; + + Assert.Equal(typeof(ContentControl), setter.Property.OwnerType); + } + } + + [Fact] + public void SetterTargetType_Should_Understand_Type_From_Xmlns() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var animation = (Animation.Animation)AvaloniaRuntimeXamlLoader.Load(xaml); + var setter = (Setter)animation.Children[0].Setters[0]; + + Assert.Equal(typeof(ContentControl), setter.Property.OwnerType); + } + } +}