Răsfoiți Sursa

Use INameScope from IServiceProvider

Nikita Tsukanov 6 ani în urmă
părinte
comite
fe5c1cec76
33 a modificat fișierele cu 305 adăugiri și 163 ștergeri
  1. 5 5
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  2. 1 3
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  3. 6 0
      src/Avalonia.Controls/Templates/FuncControlTemplate.cs
  4. 6 4
      src/Avalonia.Controls/Templates/FuncTemplate`2.cs
  5. 20 2
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  6. 2 2
      src/Avalonia.Controls/Templates/ITemplate`2.cs
  7. 0 15
      src/Avalonia.Styling/Controls/NameScope.cs
  8. 79 0
      src/Avalonia.Styling/Controls/NameScopeLocator.cs
  9. 3 71
      src/Avalonia.Styling/LogicalTree/ControlLocator.cs
  10. 2 1
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  11. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  12. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  13. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  14. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
  15. 3 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  16. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  17. 3 3
      src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
  18. 21 0
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  19. 53 6
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
  20. 13 5
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  21. 30 3
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  22. 1 1
      src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
  23. 11 3
      src/Markup/Avalonia.Markup/Data/Binding.cs
  24. 4 2
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  25. 5 2
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  26. 6 7
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
  27. 1 1
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  28. 1 1
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
  29. 1 1
      tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  30. 5 0
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs
  31. 3 3
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  32. 9 9
      tests/Avalonia.Styling.UnitTests/ControlLocatorTests.cs
  33. 1 1
      tests/Avalonia.UnitTests/TestRoot.cs

+ 5 - 5
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@@ -75,9 +75,9 @@ namespace Avalonia.Build.Tasks
                     .First(c => c.Parameters.Count == 1));
 
             var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
-            var rootServiceProviderField = asm.MainModule.ImportReference(
-                typeSystem.GetTypeReference(runtimeHelpers).Resolve().Fields
-                    .First(x => x.Name == "RootServiceProviderV1"));
+            var createRootServiceProviderMethod = asm.MainModule.ImportReference(
+                typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods
+                    .First(x => x.Name == "CreateRootServiceProviderV2"));
             
             var loaderDispatcherDef = new TypeDefinition("CompiledAvaloniaXaml", "!XamlLoader",
                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
@@ -211,7 +211,7 @@ namespace Avalonia.Build.Tasks
                             trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
                             classTypeDefinition.Methods.Add(trampoline);
 
-                            var regularStart = Instruction.Create(OpCodes.Ldsfld, rootServiceProviderField);
+                            var regularStart = Instruction.Create(OpCodes.Call, createRootServiceProviderMethod);
                             
                             trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
                             trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart));
@@ -307,7 +307,7 @@ namespace Avalonia.Build.Tasks
                                     i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor));
                                 else
                                 {
-                                    i.Add(Instruction.Create(OpCodes.Ldsfld, rootServiceProviderField));
+                                    i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod));
                                     i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
                                 }
 

+ 1 - 3
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@@ -257,12 +257,10 @@ namespace Avalonia.Controls.Primitives
                 {
                     Logger.Verbose(LogArea.Control, this, "Creating control template");
 
-                    var child = template.Build(this);
+                    var (child, nameScope) = template.Build(this);
                     ApplyTemplatedParent(child);
                     ((ISetLogicalParent)child).SetParent(this);
                     VisualChildren.Add(child);
-
-                    var nameScope = (child is StyledElement styledChild) ? NameScope.GetNameScope(styledChild) : null;
                     
                     // Existing code kinda expect to see a NameScope even if it's empty
                     if (nameScope == null)

+ 6 - 0
src/Avalonia.Controls/Templates/FuncControlTemplate.cs

@@ -20,5 +20,11 @@ namespace Avalonia.Controls.Templates
             : base(build)
         {
         }
+
+        public new ControlTemplateResult Build(ITemplatedControl param)
+        {
+            var (control, scope) = BuildWithNameScope(param);
+            return new ControlTemplateResult(control, scope);
+        }
     }
 }

+ 6 - 4
src/Avalonia.Controls/Templates/FuncTemplate`2.cs

@@ -34,13 +34,15 @@ namespace Avalonia.Controls.Templates
         /// The created control.
         /// </returns>
         public TControl Build(TParam param)
+        {
+            return BuildWithNameScope(param).Item1;
+        }
+
+        protected (TControl, INameScope) BuildWithNameScope(TParam param)
         {
             var scope = new NameScope();
             var rv = _func(param, scope);
-            // TODO: May be return the name scope alongside with the control instead?
-            if (rv is StyledElement sl)
-                NameScope.SetNameScope(sl, scope);
-            return rv;
+            return (rv, scope);
         }
     }
 }

+ 20 - 2
src/Avalonia.Controls/Templates/IControlTemplate.cs

@@ -9,7 +9,25 @@ namespace Avalonia.Controls.Templates
     /// <summary>
     /// Interface representing a template used to build a <see cref="TemplatedControl"/>.
     /// </summary>
-    public interface IControlTemplate : ITemplate<ITemplatedControl, IControl>
+    public interface IControlTemplate : ITemplate<ITemplatedControl, ControlTemplateResult>
     {
     }
-}
+
+    public class ControlTemplateResult
+    {
+        public IControl Control { get; }
+        public INameScope NameScope { get; }
+
+        public ControlTemplateResult(IControl control, INameScope nameScope)
+        {
+            Control = control;
+            NameScope = nameScope;
+        }
+
+        public void Deconstruct(out IControl control, out INameScope scope)
+        {
+            control = Control;
+            scope = NameScope;
+        }
+    }
+}

+ 2 - 2
src/Avalonia.Controls/Templates/ITemplate`2.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Controls.Templates
     /// </summary>
     /// <typeparam name="TParam">The type of the parameter.</typeparam>
     /// <typeparam name="TControl">The type of control.</typeparam>
-    public interface ITemplate<TParam, TControl> where TControl : IControl
+    public interface ITemplate<TParam, TControl>
     {
         /// <summary>
         /// Creates the control.
@@ -19,4 +19,4 @@ namespace Avalonia.Controls.Templates
         /// </returns>
         TControl Build(TParam param);
     }
-}
+}

+ 0 - 15
src/Avalonia.Styling/Controls/NameScope.cs

@@ -105,21 +105,6 @@ namespace Avalonia.Controls
                 Registered?.Invoke(this, new NameScopeEventArgs(name, element));
             }
         }
-
-        /// <summary>
-        /// Registers an element in the name scope associated with the scopeElement.
-        /// Creates the scope if one isn't associated with the element yet
-        /// </summary>
-        /// <param name="scopeElement"></param>
-        /// <param name="name"></param>
-        /// <param name="element"></param>
-        public static void Register(StyledElement scopeElement, string name, object element)
-        {
-            var scope = scopeElement as INameScope ?? GetNameScope(scopeElement);
-            if(scope == null)
-                SetNameScope(scopeElement, scope = new NameScope());
-            scope.Register(name, element);
-        }
         
         /// <summary>
         /// Finds a named element in the name scope.

+ 79 - 0
src/Avalonia.Styling/Controls/NameScopeLocator.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using Avalonia.LogicalTree;
+using Avalonia.Reactive;
+
+namespace Avalonia.Controls
+{
+    public class NameScopeLocator
+    {
+        /// <summary>
+        /// Tracks a named control relative to another control.
+        /// </summary>
+        /// <param name="relativeTo">
+        /// The control relative from which the other control should be found.
+        /// </param>
+        /// <param name="name">The name of the control to find.</param>
+        public static IObservable<object> Track(INameScope scope, string name)
+        {
+            return new ScopeTracker(scope, name);
+        }
+        
+        private class ScopeTracker : LightweightObservableBase<object>
+        {
+            private readonly string _name;
+            INameScope _nameScope;
+            object _value;
+
+            public ScopeTracker(INameScope nameScope, string name)
+            {
+                _nameScope = nameScope;
+                _name = name;
+            }
+
+
+            protected override void Initialize()
+            {
+                _nameScope.Registered += Registered;
+                _nameScope.Unregistered += Unregistered;
+                _value = _nameScope.Find<ILogical>(_name);
+            }
+
+            protected override void Deinitialize()
+            {
+                if (_nameScope != null)
+                {
+                    _nameScope.Registered -= Registered;
+                    _nameScope.Unregistered -= Unregistered;
+                }
+
+                _value = null;
+            }
+
+            protected override void Subscribed(IObserver<object> observer, bool first)
+            {
+                observer.OnNext(_value);
+            }
+
+            private void Registered(object sender, NameScopeEventArgs e)
+            {
+                if (e.Name == _name)
+                {
+                    _value = e.Element;
+                    PublishNext(_value);
+                }
+            }
+
+            private void Unregistered(object sender, NameScopeEventArgs e)
+            {
+                if (e.Name == _name)
+                {
+                    _value = null;
+                    PublishNext(null);
+                }
+            }
+
+        }
+    }
+}

+ 3 - 71
src/Avalonia.Styling/LogicalTree/ControlLocator.cs

@@ -15,18 +15,6 @@ namespace Avalonia.LogicalTree
     /// </summary>
     public static class ControlLocator
     {
-        /// <summary>
-        /// Tracks a named control relative to another control.
-        /// </summary>
-        /// <param name="relativeTo">
-        /// The control relative from which the other control should be found.
-        /// </param>
-        /// <param name="name">The name of the control to find.</param>
-        public static IObservable<ILogical> Track(ILogical relativeTo, string name)
-        {
-            return new ControlTracker(relativeTo, name);
-        }
-
         public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null)
         {
             return new ControlTracker(relativeTo, ancestorLevel, ancestorType);
@@ -35,18 +23,10 @@ namespace Avalonia.LogicalTree
         private class ControlTracker : LightweightObservableBase<ILogical>
         {
             private readonly ILogical _relativeTo;
-            private readonly string _name;
             private readonly int _ancestorLevel;
             private readonly Type _ancestorType;
-            INameScope _nameScope;
             ILogical _value;
 
-            public ControlTracker(ILogical relativeTo, string name)
-            {
-                _relativeTo = relativeTo;
-                _name = name;
-            }
-
             public ControlTracker(ILogical relativeTo, int ancestorLevel, Type ancestorType)
             {
                 _relativeTo = relativeTo;
@@ -66,12 +46,6 @@ namespace Avalonia.LogicalTree
                 _relativeTo.AttachedToLogicalTree -= Attached;
                 _relativeTo.DetachedFromLogicalTree -= Detached;
 
-                if (_nameScope != null)
-                {
-                    _nameScope.Registered -= Registered;
-                    _nameScope.Unregistered -= Unregistered;
-                }
-
                 _value = null;
             }
 
@@ -88,57 +62,15 @@ namespace Avalonia.LogicalTree
 
             private void Detached(object sender, LogicalTreeAttachmentEventArgs e)
             {
-                if (_nameScope != null)
-                {
-                    _nameScope.Registered -= Registered;
-                    _nameScope.Unregistered -= Unregistered;
-                }
-
                 _value = null;
                 PublishNext(null);
             }
 
-            private void Registered(object sender, NameScopeEventArgs e)
-            {
-                if (e.Name == _name && e.Element is ILogical logical)
-                {
-                    _value = logical;
-                    PublishNext(logical);
-                }
-            }
-
-            private void Unregistered(object sender, NameScopeEventArgs e)
-            {
-                if (e.Name == _name)
-                {
-                    _value = null;
-                    PublishNext(null);
-                }
-            }
-
             private void Update()
             {
-                if (_name != null)
-                {
-                    _nameScope = _relativeTo.FindNameScope();
-
-                    if (_nameScope != null)
-                    {
-                        _nameScope.Registered += Registered;
-                        _nameScope.Unregistered += Unregistered;
-                        _value = _nameScope.Find<ILogical>(_name);
-                    }
-                    else
-                    {
-                        _value = null;
-                    }
-                }
-                else
-                {
-                    _value = _relativeTo.GetLogicalAncestors()
-                        .Where(x => _ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
-                        .ElementAtOrDefault(_ancestorLevel);
-                }
+                _value = _relativeTo.GetLogicalAncestors()
+                    .Where(x => _ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
+                    .ElementAtOrDefault(_ancestorLevel);
             }
         }
     }

+ 2 - 1
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@@ -40,7 +40,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 Source = Source,
                 StringFormat = StringFormat,
                 RelativeSource = RelativeSource,
-                DefaultAnchor = new WeakReference(GetDefaultAnchor(descriptorContext))
+                DefaultAnchor = new WeakReference(GetDefaultAnchor(descriptorContext)),
+                NameScope = new WeakReference<INameScope>(serviceProvider.GetService<INameScope>())
             };
         }
 

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

@@ -17,6 +17,6 @@ namespace Avalonia.Markup.Xaml.Templates
 
         public Type TargetType { get; set; }
 
-        public IControl Build(ITemplatedControl control) => TemplateContent.Load(Content);
+        public ControlTemplateResult Build(ITemplatedControl control) => TemplateContent.Load(Content);
     }
-}
+}

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@@ -32,6 +32,6 @@ namespace Avalonia.Markup.Xaml.Templates
             }
         }
 
-        public IControl Build(object data) => TemplateContent.Load(Content);
+        public IControl Build(object data) => TemplateContent.Load(Content).Control;
     }
-}
+}

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs

@@ -14,8 +14,8 @@ namespace Avalonia.Markup.Xaml.Templates
         public object Content { get; set; }
 
         public IPanel Build()
-                => (IPanel)TemplateContent.Load(Content);
+                => (IPanel)TemplateContent.Load(Content).Control;
 
         object ITemplate.Build() => Build();
     }
-}
+}

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs

@@ -13,8 +13,8 @@ namespace Avalonia.Markup.Xaml.Templates
         [TemplateContent]
         public object Content { get; set; }
 
-        public IControl Build() => TemplateContent.Load(Content);
+        public IControl Build() => TemplateContent.Load(Content).Control;
 
         object ITemplate.Build() => Build();
     }
-}
+}

+ 3 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@@ -4,17 +4,18 @@
 using System;
 using Avalonia.Controls;
 using System.Collections.Generic;
+using Avalonia.Controls.Templates;
 
 namespace Avalonia.Markup.Xaml.Templates
 {
     
     public static class TemplateContent
     {
-        public static IControl Load(object templateContent)
+        public static ControlTemplateResult Load(object templateContent)
         {
             if (templateContent is Func<IServiceProvider, object> direct)
             {
-                return (IControl)direct(null);
+                return (ControlTemplateResult)direct(null);
             }
             throw new ArgumentException(nameof(templateContent));
         }

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@@ -51,9 +51,9 @@ namespace Avalonia.Markup.Xaml.Templates
 
         public IControl Build(object data)
         {
-            var visualTreeForItem = TemplateContent.Load(Content);
+            var visualTreeForItem = TemplateContent.Load(Content).Control;
             visualTreeForItem.DataContext = data;
             return visualTreeForItem;
         }
     }
-}
+}

+ 3 - 3
src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs

@@ -156,7 +156,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
                 {
                     overrideField.SetValue(null,
                         new Action<object>(
-                            target => { populateCb(XamlIlRuntimeHelpers.RootServiceProviderV1, target); }));
+                            target => { populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), target); }));
                     try
                     {
                         return Activator.CreateInstance(targetType);
@@ -170,11 +170,11 @@ namespace Avalonia.Markup.Xaml.XamlIl
                 var createCb = Expression.Lambda<Func<IServiceProvider, object>>(
                     Expression.Convert(Expression.Call(
                         created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile();
-                return createCb(XamlIlRuntimeHelpers.RootServiceProviderV1);
+                return createCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
             }
             else
             {
-                populateCb(XamlIlRuntimeHelpers.RootServiceProviderV1, rootInstance);
+                populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), rootInstance);
                 return rootInstance;
             }
         }

+ 21 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@@ -54,9 +54,30 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit,
             };
             rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv);
+            rv.ContextTypeBuilderCallback = (b, c) => EmitNameScopeField(rv, typeSystem, b, c);
             return rv;
         }
 
+        public const string ContextNameScopeFieldName = "AvaloniaNameScope";
+
+        private static void EmitNameScopeField(XamlIlLanguageTypeMappings mappings,
+            IXamlIlTypeSystem typeSystem,
+            IXamlIlTypeBuilder typebuilder, IXamlIlEmitter constructor)
+        {
+
+            var nameScopeType = typeSystem.FindType("Avalonia.Controls.INameScope");
+            var field = typebuilder.DefineField(nameScopeType, 
+                ContextNameScopeFieldName, true, false);
+            constructor
+                .Ldarg_0()
+                .Ldarg(1)
+                .Ldtype(nameScopeType)
+                .EmitCall(mappings.ServiceProvider.GetMethod(new FindMethodMethodSignature("GetService",
+                    typeSystem.FindType("System.Object"), typeSystem.FindType("System.Type"))))
+                .Stfld(field);
+        }
+        
+
         class AttributeResolver : IXamlIlCustomAttributeResolver
         {
             private readonly IXamlIlType _typeConverterAttribute;

+ 53 - 6
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs

@@ -46,10 +46,52 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                     };
             }
 
+            if (!context.ParentNodes().Any()
+                && node is XamlIlValueWithManipulationNode mnode)
+            {
+                mnode.Manipulation = new XamlIlManipulationGroupNode(mnode,
+                    new[]
+                    {
+                        mnode.Manipulation,
+                        new AddNameScopeToRootObjectXamlIlNode(mnode, context.GetAvaloniaTypes())
+                    });
+            }
             return node;
         }
 
-        
+        class AddNameScopeToRootObjectXamlIlNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode
+        {
+            private readonly AvaloniaXamlIlWellKnownTypes _types;
+
+            public AddNameScopeToRootObjectXamlIlNode(IXamlIlLineInfo lineInfo,
+                AvaloniaXamlIlWellKnownTypes types) : base(lineInfo)
+            {
+                _types = types;
+            }
+
+            public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
+            {
+                var next = codeGen.DefineLabel();
+                using (var local = codeGen.LocalsPool.GetLocal(_types.StyledElement))
+                {
+                    codeGen
+                        .Isinst(_types.StyledElement)
+                        .Dup()
+                        .Stloc(local.Local)
+                        .Brfalse(next)
+                        .Ldloc(local.Local)
+                        .Ldloc(context.ContextLocal)
+                        .Ldfld(context.RuntimeContext.ContextType.Fields.First(f =>
+                            f.Name == AvaloniaXamlIlLanguage.ContextNameScopeFieldName))
+                        .EmitCall(_types.NameScopeSetNameScope)
+                        .MarkLabel(next);
+
+                }
+
+                return XamlIlNodeEmitResult.Void(1);
+
+            }
+        }
     }
 
     class AvaloniaNameScopeRegistrationXamlIlNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode
@@ -68,19 +110,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
 
         public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
         {
+            var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
+                f.Name == AvaloniaXamlIlLanguage.ContextNameScopeFieldName);
+            
             using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object))
             {
+
                 codeGen
-                    // var target = {pop}    
+                    // var target = {pop}
                     .Stloc(targetLoc.Local)
-                    // NameScope.Register(context.IntermediateRoot, Name, target)
+                    // _context.NameScope.Register(Name, target)
                     .Ldloc(context.ContextLocal)
-                    .Ldfld(context.RuntimeContext.IntermediateRootObjectField)
-                    .Castclass(_types.StyledElement);
+                    .Ldfld(scopeField);
+                    
                 context.Emit(Name, codeGen, Name.Type.GetClrType());
+                
                 codeGen
                     .Ldloc(targetLoc.Local)
-                    .EmitCall(_types.NameScopeStaticRegister, true);
+                    .EmitCall(_types.INameScopeRegister, true);
             }
 
             return XamlIlNodeEmitResult.Void(1);

+ 13 - 5
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@@ -16,16 +16,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
         public IXamlIlMethod AvaloniaObjectSetValueMethod { get; }
         public IXamlIlType IDisposable { get; }
         public XamlIlTypeWellKnownTypes XamlIlTypes { get; }
+        public XamlIlLanguageTypeMappings XamlIlMappings { get; }
         public IXamlIlType Transitions { get; }
         public IXamlIlType AssignBindingAttribute { get; }
         public IXamlIlType UnsetValueType { get; }
         public IXamlIlType StyledElement { get; }
         public IXamlIlType NameScope { get; }
-        public IXamlIlMethod NameScopeStaticRegister { get; }
+        public IXamlIlMethod NameScopeSetNameScope { get; }
+        public IXamlIlType INameScope { get; }
+        public IXamlIlMethod INameScopeRegister { get; }
         
         public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx)
         {
             XamlIlTypes = ctx.Configuration.WellKnownTypes;
+            XamlIlMappings = ctx.Configuration.TypeMappings;
             AvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObject");
             IAvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.IAvaloniaObject");
             AvaloniaObjectExtensions = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions");
@@ -41,16 +45,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
                 IBinding, ctx.Configuration.WellKnownTypes.Object);
             UnsetValueType = ctx.Configuration.TypeSystem.GetType("Avalonia.UnsetValueType");
             StyledElement = ctx.Configuration.TypeSystem.GetType("Avalonia.StyledElement");
-            NameScope = ctx.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScope");
-            NameScopeStaticRegister = NameScope.FindMethod(
+            INameScope = ctx.Configuration.TypeSystem.GetType("Avalonia.Controls.INameScope");
+            INameScopeRegister = INameScope.GetMethod(
                 new FindMethodMethodSignature("Register", XamlIlTypes.Void,
-                     StyledElement, XamlIlTypes.String, XamlIlTypes.Object)
+                     XamlIlTypes.String, XamlIlTypes.Object)
                 {
-                    IsStatic = true, DeclaringOnly = true, IsExactMatch = true
+                    IsStatic = false, DeclaringOnly = true, IsExactMatch = true
                 });
+            NameScope = ctx.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScope");
+            NameScopeSetNameScope = NameScope.GetMethod(new FindMethodMethodSignature("SetNameScope",
+                XamlIlTypes.Void, StyledElement, INameScope) {IsStatic = true});
 
             AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", XamlIlTypes.Void,
                 false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority);
+            
         }
     }
 

+ 30 - 3
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@@ -1,8 +1,10 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Reflection;
 using Avalonia.Controls;
+using Avalonia.Controls.Templates;
 using Avalonia.Data;
 // ReSharper disable UnusedMember.Global
 // ReSharper disable UnusedParameter.Global
@@ -17,7 +19,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
             var resourceNodes = provider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents
                 .OfType<IResourceNode>().ToList();
             var rootObject = provider.GetService<IRootObjectProvider>().RootObject;
-            return sp => builder(new DeferredParentServiceProvider(sp, resourceNodes, rootObject));
+            return sp =>
+            {
+                var scope = new NameScope();
+                var obj =  builder(new DeferredParentServiceProvider(sp, resourceNodes, rootObject, scope));
+                return new ControlTemplateResult((IControl)obj, scope);
+            };
         }
 
         class DeferredParentServiceProvider :
@@ -27,12 +34,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
         {
             private readonly IServiceProvider _parentProvider;
             private readonly List<IResourceNode> _parentResourceNodes;
+            private readonly INameScope _nameScope;
 
             public DeferredParentServiceProvider(IServiceProvider parentProvider, List<IResourceNode> parentResourceNodes,
-                object rootObject)
+                object rootObject, INameScope nameScope)
             {
                 _parentProvider = parentProvider;
                 _parentResourceNodes = parentResourceNodes;
+                _nameScope = nameScope;
                 RootObject = rootObject;
             }
 
@@ -48,6 +57,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
 
             public object GetService(Type serviceType)
             {
+                if (serviceType == typeof(INameScope))
+                    return _nameScope;
                 if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
                     return this;
                 if (serviceType == typeof(IRootObjectProvider))
@@ -133,12 +144,28 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
             }
         }
         
-        public static readonly IServiceProvider RootServiceProviderV1 = new RootServiceProvider();
+        [Obsolete("Don't use", true)]
+        public static readonly IServiceProvider RootServiceProviderV1 = new RootServiceProvider(null);
 
+        [DebuggerStepThrough]
+        public static IServiceProvider CreateRootServiceProviderV2()
+        {
+            return new RootServiceProvider(new NameScope());
+        }
+        
         class RootServiceProvider : IServiceProvider, IAvaloniaXamlIlParentStackProvider
         {
+            private readonly INameScope _nameScope;
+
+            public RootServiceProvider(INameScope nameScope)
+            {
+                _nameScope = nameScope;
+            }
+            
             public object GetService(Type serviceType)
             {
+                if (serviceType == typeof(INameScope))
+                    return _nameScope;
                 if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
                     return this;
                 return null;

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github

@@ -1 +1 @@
-Subproject commit fc52295ea6c45f709bf63975026a4a18ce230be9
+Subproject commit c2ec091f79fb4e1eea629bc823c9c24da7050022

+ 11 - 3
src/Markup/Avalonia.Markup/Data/Binding.cs

@@ -5,6 +5,7 @@ using System;
 using System.Linq;
 using System.Reactive;
 using System.Reactive.Linq;
+using Avalonia.Controls;
 using Avalonia.Data.Converters;
 using Avalonia.Data.Core;
 using Avalonia.LogicalTree;
@@ -90,6 +91,8 @@ namespace Avalonia.Data
         public string StringFormat { get; set; }
 
         public WeakReference DefaultAnchor { get; set; }
+        
+        public WeakReference<INameScope> NameScope { get; set; }
 
         /// <summary>
         /// Gets or sets a function used to resolve types from names in the binding path.
@@ -110,7 +113,9 @@ namespace Avalonia.Data
             
             ExpressionObserver observer;
 
-            var (node, mode)  = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver);
+            INameScope nameScope = null;
+            NameScope?.TryGetTarget(out nameScope);
+            var (node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver, nameScope);
 
             if (ElementName != null)
             {
@@ -254,9 +259,12 @@ namespace Avalonia.Data
             ExpressionNode node)
         {
             Contract.Requires<ArgumentNullException>(target != null);
-            
+
+            NameScope.TryGetTarget(out var scope);
+            if (scope == null)
+                throw new InvalidOperationException("Name scope is null or was already collected");
             var result = new ExpressionObserver(
-                ControlLocator.Track(target, elementName),
+                NameScopeLocator.Track(scope, elementName),
                 node,
                 null);
             return result;

+ 4 - 2
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Reactive;
+using Avalonia.Controls;
 using Avalonia.Data.Core;
 using Avalonia.Utilities;
 
@@ -7,7 +8,8 @@ namespace Avalonia.Markup.Parsers
 {
     public static class ExpressionObserverBuilder
     {
-        internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type> typeResolver = null)
+        internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type> typeResolver = null,
+            INameScope nameScope = null)
         {
             if (string.IsNullOrWhiteSpace(expression))
             {
@@ -15,7 +17,7 @@ namespace Avalonia.Markup.Parsers
             }
             
             var reader = new CharacterReader(expression.AsSpan());
-            var parser = new ExpressionParser(enableValidation, typeResolver);
+            var parser = new ExpressionParser(enableValidation, typeResolver, nameScope);
             var node = parser.Parse(ref reader);
 
             if (!reader.End)

+ 5 - 2
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@@ -7,6 +7,7 @@ using Avalonia.Utilities;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Avalonia.Controls;
 
 namespace Avalonia.Markup.Parsers
 {
@@ -20,10 +21,12 @@ namespace Avalonia.Markup.Parsers
     {
         private readonly bool _enableValidation;
         private readonly Func<string, string, Type> _typeResolver;
+        private readonly INameScope _nameScope;
 
-        public ExpressionParser(bool enableValidation, Func<string, string, Type> typeResolver)
+        public ExpressionParser(bool enableValidation, Func<string, string, Type> typeResolver, INameScope nameScope)
         {
             _typeResolver = typeResolver;
+            _nameScope = nameScope;
             _enableValidation = enableValidation;
         }
 
@@ -213,7 +216,7 @@ namespace Avalonia.Markup.Parsers
                 throw new ExpressionParseException(r.Position, "Element name expected after '#'.");
             }
 
-            nodes.Add(new ElementNameNode(name.ToString()));
+            nodes.Add(new ElementNameNode(_nameScope, name.ToString()));
             return State.AfterMember;
         }
 

+ 6 - 7
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs

@@ -1,4 +1,5 @@
 using System;
+using Avalonia.Controls;
 using Avalonia.Data.Core;
 using Avalonia.LogicalTree;
 
@@ -6,11 +7,13 @@ namespace Avalonia.Markup.Parsers.Nodes
 {
     internal class ElementNameNode : ExpressionNode
     {
+        private readonly WeakReference<INameScope> _nameScope;
         private readonly string _name;
         private IDisposable _subscription;
 
-        public ElementNameNode(string name)
+        public ElementNameNode(INameScope nameScope, string name)
         {
+            _nameScope = new WeakReference<INameScope>(nameScope);
             _name = name;
         }
 
@@ -18,14 +21,10 @@ namespace Avalonia.Markup.Parsers.Nodes
 
         protected override void StartListeningCore(WeakReference reference)
         {
-            if (reference.Target is ILogical logical)
-            {
-                _subscription = ControlLocator.Track(logical, _name).Subscribe(ValueChanged);
-            }
+            if (_nameScope.TryGetTarget(out var scope))
+                _subscription = NameScopeLocator.Track(scope, _name).Subscribe(ValueChanged);
             else
-            {
                 _subscription = null;
-            }
         }
 
         protected override void StopListeningCore()

+ 1 - 1
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@@ -472,7 +472,7 @@ namespace Avalonia.Controls.UnitTests
             Assert.Null(NameScope.GetNameScope((TextBlock)item));
         }
 
-        [Fact]
+        [Fact(Skip="We are using name scopes from IServiceProvider now")]
         public void DataTemplate_Created_Content_Should_Be_NameScope()
         {
             var items = new object[]

+ 1 - 1
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs

@@ -136,7 +136,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
             Assert.Null(NameScope.GetNameScope((Control)target.Child));
         }
 
-        [Fact]
+        [Fact(Skip = "We are using name scopes from IServiceProvider now")]
         public void DataTemplate_Created_Control_Should_Be_NameScope()
         {
             var (target, _) = CreateTarget();

+ 1 - 1
tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@@ -92,7 +92,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Empty(target.GetLogicalChildren());
         }
 
-        [Fact]
+        [Fact(Skip = "We are using name scopes from IServiceProvider now")]
         public void Templated_Child_Should_Be_NameScope()
         {
             var target = new TemplatedControl

+ 5 - 0
tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs

@@ -1,6 +1,7 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
 using Avalonia.Controls;
 using Avalonia.Data;
 using Avalonia.Markup.Data;
@@ -40,6 +41,7 @@ namespace Avalonia.Markup.UnitTests.Data
             {
                 ElementName = "source",
                 Path = "Text",
+                NameScope = new WeakReference<INameScope>(NameScope.GetNameScope(root))
             };
 
             target.Bind(TextBox.TextProperty, binding);
@@ -76,6 +78,7 @@ namespace Avalonia.Markup.UnitTests.Data
             var binding = new Binding
             {
                 ElementName = "source",
+                NameScope = new WeakReference<INameScope>(NameScope.GetNameScope(root))
             };
 
             target.Bind(ContentControl.ContentProperty, binding);
@@ -108,6 +111,7 @@ namespace Avalonia.Markup.UnitTests.Data
             {
                 ElementName = "source",
                 Path = "Text",
+                NameScope = new WeakReference<INameScope>(NameScope.GetNameScope(root))
             };
 
             target.Bind(TextBox.TextProperty, binding);
@@ -145,6 +149,7 @@ namespace Avalonia.Markup.UnitTests.Data
             var binding = new Binding
             {
                 ElementName = "source",
+                NameScope = new WeakReference<INameScope>(NameScope.GetNameScope(root))
             };
 
             target.Bind(ContentControl.ContentProperty, binding);

+ 3 - 3
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@@ -296,7 +296,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 ";
             var template = AvaloniaXamlLoader.Parse<ControlTemplate>(xaml);
 
-            var parent = (ContentControl)template.Build(new ContentControl());
+            var parent = (ContentControl)template.Build(new ContentControl()).Control;
 
             Assert.Equal("parent", parent.Name);
 
@@ -320,7 +320,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 ";
             var template = AvaloniaXamlLoader.Parse<ControlTemplate>(xaml);
 
-            var panel = (Panel)template.Build(new ContentControl());
+            var panel = (Panel)template.Build(new ContentControl()).Control;
 
             Assert.Equal(2, panel.Children.Count);
 
@@ -681,7 +681,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
             var control = new ContentControl();
 
-            var result = (ContentPresenter)template.Build(control);
+            var result = (ContentPresenter)template.Build(control).Control;
 
             Assert.NotNull(result);
         }

+ 9 - 9
tests/Avalonia.Styling.UnitTests/ControlLocatorTests.cs

@@ -34,7 +34,7 @@ namespace Avalonia.Styling.UnitTests
             var scope = Register(root, relativeTo);
             Register(root, target);
             
-            var locator = ControlLocator.Track(relativeTo, "target");
+            var locator = NameScopeLocator.Track(scope, "target");
             var result = await locator.Take(1);
 
             Assert.Same(target, result);
@@ -65,11 +65,11 @@ namespace Avalonia.Styling.UnitTests
             };
             var scope = Register(root, relativeTo);
 
-            var locator = ControlLocator.Track(relativeTo, "target");
+            var locator = NameScopeLocator.Track(scope, "target");
             var target = new TextBlock { Name = "target" };
             var result = new List<ILogical>();
 
-            using (locator.Subscribe(x => result.Add(x)))
+            using (locator.Subscribe(x => result.Add((ILogical)x)))
             {
                 panel.Children.Add(target);
                 Register(root, target);
@@ -101,9 +101,9 @@ namespace Avalonia.Styling.UnitTests
             var scope = Register(root, target);
             Register(root, relativeTo);
             
-            var locator = ControlLocator.Track(relativeTo, "target");
+            var locator = NameScopeLocator.Track(scope, "target");
             var result = new List<ILogical>();
-            locator.Subscribe(x => result.Add(x));
+            locator.Subscribe(x => result.Add((IControl)x));
 
             var other = new TextBlock { Name = "target" };
             panel.Children.Remove(target);
@@ -114,7 +114,7 @@ namespace Avalonia.Styling.UnitTests
             Assert.Equal(new[] { target, null, other }, result);
         }
 
-        [Fact]
+        [Fact(Skip = "I'm going to remove that logic anyway")]
         public void Track_By_Name_Should_Find_Control_When_Tree_Changed()
         {
             TextBlock target1;
@@ -151,10 +151,10 @@ namespace Avalonia.Styling.UnitTests
             };
             var scope2 = Register(root2, target2);
 
-            var locator = ControlLocator.Track(relativeTo, "target");
+            var locator = NameScopeLocator.Track(scope1, "target");
             var result = new List<ILogical>();
 
-            using (locator.Subscribe(x => result.Add(x)))
+            using (locator.Subscribe(x => result.Add((ILogical)x)))
             {
                 ((StackPanel)root1.Child).Children.Remove(relativeTo);
                 scope1.Unregister(relativeTo.Name);
@@ -174,7 +174,7 @@ namespace Avalonia.Styling.UnitTests
             var scope = (TrackingNameScope)NameScope.GetNameScope(anchor);
             if (scope == null)
                 NameScope.SetNameScope(anchor, scope = new TrackingNameScope());
-            NameScope.Register(anchor, element.Name, element);
+            scope.Register(element.Name, element);
             return scope;
         }
         

+ 1 - 1
tests/Avalonia.UnitTests/TestRoot.cs

@@ -88,7 +88,7 @@ namespace Avalonia.UnitTests
                 if (element.Name != null)
                 {
                     if (scope.Find(element.Name) != element)
-                        NameScope.Register(this, element.Name, element);
+                        scope.Register(element.Name, element);
                 }
 
                 if(element is IVisual visual && (force || NameScope.GetNameScope(element) == null))