diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index c54d8e19a1..0dd52c9b48 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/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));
}
diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
index 2927a3c7b3..47c3240374 100644
--- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs
+++ b/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)
diff --git a/src/Avalonia.Controls/Templates/FuncControlTemplate.cs b/src/Avalonia.Controls/Templates/FuncControlTemplate.cs
index 8a588b4089..19458cc6b8 100644
--- a/src/Avalonia.Controls/Templates/FuncControlTemplate.cs
+++ b/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);
+ }
}
}
diff --git a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs
index a779f796e7..9c2e77c325 100644
--- a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs
+++ b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs
@@ -34,13 +34,15 @@ namespace Avalonia.Controls.Templates
/// The created control.
///
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);
}
}
}
diff --git a/src/Avalonia.Controls/Templates/IControlTemplate.cs b/src/Avalonia.Controls/Templates/IControlTemplate.cs
index 408329c450..a3ef0fa954 100644
--- a/src/Avalonia.Controls/Templates/IControlTemplate.cs
+++ b/src/Avalonia.Controls/Templates/IControlTemplate.cs
@@ -9,7 +9,25 @@ namespace Avalonia.Controls.Templates
///
/// Interface representing a template used to build a .
///
- public interface IControlTemplate : ITemplate
+ public interface IControlTemplate : ITemplate
{
}
-}
\ No newline at end of file
+
+ 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;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Templates/ITemplate`2.cs b/src/Avalonia.Controls/Templates/ITemplate`2.cs
index f61292c64b..a0c0e88eca 100644
--- a/src/Avalonia.Controls/Templates/ITemplate`2.cs
+++ b/src/Avalonia.Controls/Templates/ITemplate`2.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Controls.Templates
///
/// The type of the parameter.
/// The type of control.
- public interface ITemplate where TControl : IControl
+ public interface ITemplate
{
///
/// Creates the control.
@@ -19,4 +19,4 @@ namespace Avalonia.Controls.Templates
///
TControl Build(TParam param);
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs
index aeafa0bb0c..afafa03772 100644
--- a/src/Avalonia.Styling/Controls/NameScope.cs
+++ b/src/Avalonia.Styling/Controls/NameScope.cs
@@ -105,21 +105,6 @@ namespace Avalonia.Controls
Registered?.Invoke(this, new NameScopeEventArgs(name, element));
}
}
-
- ///
- /// Registers an element in the name scope associated with the scopeElement.
- /// Creates the scope if one isn't associated with the element yet
- ///
- ///
- ///
- ///
- 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);
- }
///
/// Finds a named element in the name scope.
diff --git a/src/Avalonia.Styling/Controls/NameScopeLocator.cs b/src/Avalonia.Styling/Controls/NameScopeLocator.cs
new file mode 100644
index 0000000000..024fdb70a5
--- /dev/null
+++ b/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
+ {
+ ///
+ /// Tracks a named control relative to another control.
+ ///
+ ///
+ /// The control relative from which the other control should be found.
+ ///
+ /// The name of the control to find.
+ public static IObservable
public static class ControlLocator
{
- ///
- /// Tracks a named control relative to another control.
- ///
- ///
- /// The control relative from which the other control should be found.
- ///
- /// The name of the control to find.
- public static IObservable Track(ILogical relativeTo, string name)
- {
- return new ControlTracker(relativeTo, name);
- }
-
public static IObservable 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
{
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(_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);
}
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
index 726f4221f8..a466714136 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
+++ b/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(serviceProvider.GetService())
};
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
index e28828625f..f46ee8787b 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
+++ b/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);
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
index ec5de8a059..c81a3718a7 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
+++ b/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;
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
index 6e4d2a3980..0dd4e57a7f 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
+++ b/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();
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
index b8fec4e8d0..5f36cfe0ac 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
+++ b/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();
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
index ead373d380..6a30c3861a 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
+++ b/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 direct)
{
- return (IControl)direct(null);
+ return (ControlTemplateResult)direct(null);
}
throw new ArgumentException(nameof(templateContent));
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
index bd2b9d2efd..0e2a131afd 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
+++ b/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;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
index 167b75603f..b91d679fba 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
@@ -156,7 +156,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
{
overrideField.SetValue(null,
new Action(
- 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>(
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;
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
index b4d090e278..ea9eaee73a 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
+++ b/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;
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 05548808af..d19770b8bd 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
+++ b/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);
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
index 81bbc80851..7ddd815e47 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
+++ b/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);
+
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
index c6d2ecee72..0b0e1c5c5e 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
+++ b/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().Parents
.OfType().ToList();
var rootObject = provider.GetService().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 _parentResourceNodes;
+ private readonly INameScope _nameScope;
public DeferredParentServiceProvider(IServiceProvider parentProvider, List 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;
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
index fc52295ea6..c2ec091f79 160000
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
@@ -1 +1 @@
-Subproject commit fc52295ea6c45f709bf63975026a4a18ce230be9
+Subproject commit c2ec091f79fb4e1eea629bc823c9c24da7050022
diff --git a/src/Markup/Avalonia.Markup/Data/Binding.cs b/src/Markup/Avalonia.Markup/Data/Binding.cs
index 0b85e3224d..dbe5800e55 100644
--- a/src/Markup/Avalonia.Markup/Data/Binding.cs
+++ b/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 NameScope { get; set; }
///
/// 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(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;
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
index 8cb14277ff..f957bcab1e 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
+++ b/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 typeResolver = null)
+ internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func 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)
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
index a1350a8393..ed3e65feda 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
+++ b/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 _typeResolver;
+ private readonly INameScope _nameScope;
- public ExpressionParser(bool enableValidation, Func typeResolver)
+ public ExpressionParser(bool enableValidation, Func 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;
}
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
index f09efca7d0..981e93c534 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
+++ b/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 _nameScope;
private readonly string _name;
private IDisposable _subscription;
- public ElementNameNode(string name)
+ public ElementNameNode(INameScope nameScope, string name)
{
+ _nameScope = new WeakReference(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()
diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
index 6680766ffa..d5245869b3 100644
--- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
+++ b/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[]
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
index b8b329099a..020de2880e 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
+++ b/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();
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
index db0f2b6fb3..cd5cee1329 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
+++ b/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
diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs
index e2a5824102..0019b06911 100644
--- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs
+++ b/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(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(NameScope.GetNameScope(root))
};
target.Bind(ContentControl.ContentProperty, binding);
@@ -108,6 +111,7 @@ namespace Avalonia.Markup.UnitTests.Data
{
ElementName = "source",
Path = "Text",
+ NameScope = new WeakReference(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(NameScope.GetNameScope(root))
};
target.Bind(ContentControl.ContentProperty, binding);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
index 720ff3b0de..d74eed992e 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
@@ -296,7 +296,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
var template = AvaloniaXamlLoader.Parse(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(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);
}
diff --git a/tests/Avalonia.Styling.UnitTests/ControlLocatorTests.cs b/tests/Avalonia.Styling.UnitTests/ControlLocatorTests.cs
index 8440652d0a..8806cad28b 100644
--- a/tests/Avalonia.Styling.UnitTests/ControlLocatorTests.cs
+++ b/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();
- 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();
- 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();
- 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;
}
diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs
index 5ad67969c4..969d7bc821 100644
--- a/tests/Avalonia.UnitTests/TestRoot.cs
+++ b/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))