diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs index 87b5ec72fe..c7bcfdb6b5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs @@ -10,42 +10,49 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) { - if (node is XamlIlPropertyAssignmentNode pa - && pa.Property.Name == "Name" - && pa.Property.DeclaringType.FullName == "Avalonia.StyledElement") + if (node is XamlIlPropertyAssignmentNode pa) { - if (context.ParentNodes().FirstOrDefault() is XamlIlManipulationGroupNode mg - && mg.Children.OfType().Any()) - return node; + if (pa.Property.Name == "Name" + && pa.Property.DeclaringType.FullName == "Avalonia.StyledElement") + { + if (context.ParentNodes().FirstOrDefault() is XamlIlManipulationGroupNode mg + && mg.Children.OfType().Any()) + return node; - IXamlIlAstValueNode value = null; - for (var c = 0; c < pa.Values.Count; c++) - if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String)) - { - value = pa.Values[c]; - if (!(value is XamlIlAstTextNode)) + IXamlIlAstValueNode value = null; + for (var c = 0; c < pa.Values.Count; c++) + if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String)) { - var local = new XamlIlAstCompilerLocalNode(value); - // Wrap original in local initialization - pa.Values[c] = new XamlIlAstLocalInitializationNodeEmitter(value, value, local); - // Use local - value = local; - } + value = pa.Values[c]; + if (!(value is XamlIlAstTextNode)) + { + var local = new XamlIlAstCompilerLocalNode(value); + // Wrap original in local initialization + pa.Values[c] = new XamlIlAstLocalInitializationNodeEmitter(value, value, local); + // Use local + value = local; + } - break; - } + break; + } - if (value != null) - { - var objectType = context.ParentNodes().OfType().FirstOrDefault()?.Type.GetClrType(); - return new XamlIlManipulationGroupNode(pa) + if (value != null) { - Children = + var objectType = context.ParentNodes().OfType().FirstOrDefault()?.Type.GetClrType(); + return new XamlIlManipulationGroupNode(pa) { - pa, - new ScopeRegistrationNode(value, objectType) - } - }; + Children = + { + pa, + new ScopeRegistrationNode(value, objectType) + } + }; + } + } + else if (pa.Property.CustomAttributes.Select(attr => attr.Type).Intersect(context.Configuration.TypeMappings.DeferredContentPropertyAttributes).Any()) + { + pa.Values[pa.Values.Count - 1] = + new NestedScopeMetadataNode(pa.Values[pa.Values.Count - 1]); } } @@ -53,6 +60,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } } + class NestedScopeMetadataNode : XamlIlValueWithSideEffectNodeBase + { + public NestedScopeMetadataNode(IXamlIlAstValueNode value) : base(value, value) + { + } + } + class ScopeRegistrationNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode { public IXamlIlAstValueNode Value { get; set; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs index 608e68bf10..b1618e3621 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs @@ -14,6 +14,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (node is AvaloniaXamlIlDataContextTypeMetadataNode dataContextTypeMetadata) return dataContextTypeMetadata.Value; + if (node is NestedScopeMetadataNode nestedScope) + return nestedScope.Value; + return node; } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs index 598f9175e8..14c137206f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -180,7 +180,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions nodes.Add(new FindAncestorPathElementNode(GetType(ancestor.Namespace, ancestor.TypeName), ancestor.Level)); break; case BindingExpressionGrammar.NameNode elementName: - var elementType = ScopeRegistrationFinder.GetControlType(context, context.RootObject, elementName.Name); + IXamlIlType elementType = null; + foreach (var deferredContent in context.ParentNodes().OfType()) + { + elementType = ScopeRegistrationFinder.GetControlType(deferredContent, elementName.Name); + if (!(elementType is null)) + { + break; + } + } + if (elementType is null) + { + elementType = ScopeRegistrationFinder.GetControlType(context.RootObject, elementName.Name); + } + if (elementType is null) { throw new XamlIlParseException($"Unable to find element '{elementName.Name}' in the current namescope. Unable to use a compiled binding with a name binding if the name cannot be found at compile time.", lineInfo); @@ -199,8 +212,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } - class ScopeRegistrationFinder : IXamlIlAstTransformer + class ScopeRegistrationFinder : IXamlIlAstVisitor { + private Stack _stack = new Stack(); + private Stack _childScopesStack = new Stack(); + private ScopeRegistrationFinder(string name) { Name = name; @@ -210,16 +226,34 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions IXamlIlType ControlType { get; set; } - public static IXamlIlType GetControlType(XamlIlAstTransformationContext context, IXamlIlAstNode namescopeRoot, string name) + public static IXamlIlType GetControlType(IXamlIlAstNode namescopeRoot, string name) { var finder = new ScopeRegistrationFinder(name); - context.Visit(namescopeRoot, finder); + namescopeRoot.Visit(finder); return finder.ControlType; } - IXamlIlAstNode IXamlIlAstTransformer.Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) + void IXamlIlAstVisitor.Pop() + { + var node = _stack.Pop(); + if (_childScopesStack.Count > 0 && node == _childScopesStack.Peek()) + { + _childScopesStack.Pop(); + } + } + + void IXamlIlAstVisitor.Push(IXamlIlAstNode node) + { + _stack.Push(node); + if (node is NestedScopeMetadataNode) + { + _childScopesStack.Push(node); + } + } + + IXamlIlAstNode IXamlIlAstVisitor.Visit(IXamlIlAstNode node) { - if (node is ScopeRegistrationNode registration) + if (_childScopesStack.Count == 0 && node is ScopeRegistrationNode registration) { if (registration.Value is XamlIlAstTextNode text && text.Text == Name) {