Browse Source

CompiledBinding resolve template type from selector

Max Katz 2 năm trước cách đây
mục cha
commit
ddeb27e0a3

+ 2 - 3
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlMetadataRemover.cs

@@ -1,4 +1,3 @@
-using System.Linq;
 using XamlX.Ast;
 using XamlX.Ast;
 using XamlX.Transform;
 using XamlX.Transform;
 
 
@@ -8,8 +7,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
     {
     {
         public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
         public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
         {
         {
-            if (node is AvaloniaXamlIlTargetTypeMetadataNode targetType)
-                return targetType.Value;
+            while (node is AvaloniaXamlIlTargetTypeMetadataNode targetType)
+                node = targetType.Value;
 
 
             return node;
             return node;
         }
         }

+ 43 - 10
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

@@ -128,13 +128,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                                 break;
                                 break;
                             }
                             }
                         case SelectorGrammar.ChildSyntax child:
                         case SelectorGrammar.ChildSyntax child:
-                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.Child);
+                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.CombinatorSelectorType.Child);
                             break;
                             break;
                         case SelectorGrammar.DescendantSyntax descendant:
                         case SelectorGrammar.DescendantSyntax descendant:
-                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.Descendant);
+                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.CombinatorSelectorType.Descendant);
                             break;
                             break;
                         case SelectorGrammar.TemplateSyntax template:
                         case SelectorGrammar.TemplateSyntax template:
-                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.SelectorType.Template);
+                            result = new XamlIlCombinatorSelector(result, XamlIlCombinatorSelector.CombinatorSelectorType.Template);
                             break;
                             break;
                         case SelectorGrammar.NotSyntax not:
                         case SelectorGrammar.NotSyntax not:
                             result = new XamlIlNotSelector(result, Create(not.Argument, typeResolver));
                             result = new XamlIlNotSelector(result, Create(not.Argument, typeResolver));
@@ -186,18 +186,50 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                 => TypeReferenceResolver.ResolveType(context, $"{p}:{n}", true, node, true));
                 => TypeReferenceResolver.ResolveType(context, $"{p}:{n}", true, node, true));
             pn.Values[0] = selector;
             pn.Values[0] = selector;
 
 
-            return new AvaloniaXamlIlTargetTypeMetadataNode(on,
+            var templateType = GetLastTemplateTypeFromSelector(selector);
+            
+            var styleNode = new AvaloniaXamlIlTargetTypeMetadataNode(on,
                 new XamlAstClrTypeReference(selector, selector.TargetType, false),
                 new XamlAstClrTypeReference(selector, selector.TargetType, false),
                 AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
                 AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
+
+            return templateType switch
+            {
+                null => styleNode,
+                _ => new AvaloniaXamlIlTargetTypeMetadataNode(styleNode,
+                    new XamlAstClrTypeReference(styleNode, templateType, false),
+                    AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate)
+            };
         }
         }
 
 
-    }
+        private static IXamlType GetLastTemplateTypeFromSelector(XamlIlSelectorNode node)
+        {
+            while (node is not null)
+            {
+                if (node is XamlIlCombinatorSelector
+                    {
+                        SelectorType: XamlIlCombinatorSelector.CombinatorSelectorType.Template
+                    } templateSelector)
+                {
+                    if (templateSelector.TargetType is {} targetType)
+                    {
+                        return targetType;
+                    }
+                    if (templateSelector.Previous is XamlIlNestingSelector nestingSelector)
+                    {
+                        return nestingSelector.TargetType;
+                    }
+                    return null;
+                }
+                node = node.Previous;
+            }
 
 
+            return null;
+        }
+    }
 
 
-    
     abstract class XamlIlSelectorNode : XamlAstNode, IXamlAstValueNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
     abstract class XamlIlSelectorNode : XamlAstNode, IXamlAstValueNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
     {
     {
-        protected XamlIlSelectorNode Previous { get; }
+        internal XamlIlSelectorNode Previous { get; }
         public abstract IXamlType TargetType { get; }
         public abstract IXamlType TargetType { get; }
 
 
         public XamlIlSelectorNode(XamlIlSelectorNode previous,
         public XamlIlSelectorNode(XamlIlSelectorNode previous,
@@ -289,19 +321,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
 
 
     class XamlIlCombinatorSelector : XamlIlSelectorNode
     class XamlIlCombinatorSelector : XamlIlSelectorNode
     {
     {
-        private readonly SelectorType _type;
+        private readonly CombinatorSelectorType _type;
 
 
-        public enum SelectorType
+        public enum CombinatorSelectorType
         {
         {
             Child,
             Child,
             Descendant,
             Descendant,
             Template
             Template
         }
         }
-        public XamlIlCombinatorSelector(XamlIlSelectorNode previous, SelectorType type) : base(previous)
+        public XamlIlCombinatorSelector(XamlIlSelectorNode previous, CombinatorSelectorType type) : base(previous)
         {
         {
             _type = type;
             _type = type;
         }
         }
 
 
+        public CombinatorSelectorType SelectorType => _type;
         public override IXamlType TargetType => null;
         public override IXamlType TargetType => null;
         protected override void DoEmit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
         protected override void DoEmit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
         {
         {

+ 34 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@@ -754,6 +754,40 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             }
             }
         }
         }
         
         
+        [Fact]
+        public void ResolvesRelativeSourceBindingFromStyleSelector()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var xaml = @"
+<TextBox xmlns='https://github.com/avaloniaui'
+         xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+         InnerLeftContent='Hello'>
+    <TextBox.Styles>
+        <Style Selector='TextBox'>
+            <Setter Property='Template'>
+                <ControlTemplate>
+                    <StackPanel>
+                        <ContentPresenter x:Name='Content' />
+                        <TextPresenter x:Name='PART_TextPresenter' />
+                    </StackPanel>
+                </ControlTemplate>
+            </Setter>
+            <Style Selector='^ /template/ ContentPresenter#Content'>
+                <Setter Property='Content' Value='{CompiledBinding InnerLeftContent, RelativeSource={RelativeSource TemplatedParent}}' />
+            </Style>
+        </Style>
+    </TextBox.Styles>
+</TextBox>";
+
+                var textBox = AvaloniaRuntimeXamlLoader.Parse<TextBox>(xaml);
+                textBox.Measure(new Size(10, 10));
+                
+                var result = textBox.GetTemplateChildren().OfType<ContentPresenter>().First();
+                Assert.Equal(textBox.InnerLeftContent, result.Content);
+            }
+        }
+        
         [Fact]
         [Fact]
         public void ResolvesElementNameInTemplate()
         public void ResolvesElementNameInTemplate()
         {
         {