瀏覽代碼

Pass parent's service provider to the StyleInclude, so parents stack is complete

Max Katz 2 年之前
父節點
當前提交
de9e9580c0

+ 1 - 0
src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs

@@ -7,6 +7,7 @@ namespace Avalonia.Build.Tasks
         LegacyResmScheme = 3,
         TransformError = 4,
         EmitError = 4,
+        Loader = 5,
 
         Unknown = 9999
     }

+ 94 - 25
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@@ -196,7 +196,8 @@ namespace Avalonia.Build.Tasks
             var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
             var createRootServiceProviderMethod = asm.MainModule.ImportReference(
                 typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods
-                    .First(x => x.Name == "CreateRootServiceProviderV2"));
+                    .First(x => x.Name == "CreateRootServiceProviderV3"));
+            var serviceProviderType = createRootServiceProviderMethod.ReturnType;
             
             var loaderDispatcherDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!XamlLoader",
                 TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
@@ -212,11 +213,36 @@ namespace Avalonia.Build.Tasks
                 MethodAttributes.Static | MethodAttributes.Public,
                 asm.MainModule.TypeSystem.Object)
             {
-                Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)}
+                Parameters =
+                {
+                    new ParameterDefinition(serviceProviderType),
+                    new ParameterDefinition(asm.MainModule.TypeSystem.String)
+                },
+            };
+            var loaderDispatcherMethodOld = new MethodDefinition("TryLoad",
+                MethodAttributes.Static | MethodAttributes.Public,
+                asm.MainModule.TypeSystem.Object)
+            {
+                Parameters =
+                {
+                    new ParameterDefinition(asm.MainModule.TypeSystem.String)
+                },
+                Body =
+                {
+                    Instructions =
+                    {
+                        Instruction.Create(OpCodes.Ldnull),
+                        Instruction.Create(OpCodes.Ldarg_0),
+                        Instruction.Create(OpCodes.Call, loaderDispatcherMethod),
+                        Instruction.Create(OpCodes.Ret)
+                    }
+                }
             };
             loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
+            loaderDispatcherDef.Methods.Add(loaderDispatcherMethodOld);
             asm.MainModule.Types.Add(loaderDispatcherDef);
 
+
             var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
                 m =>
                     m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 3 &&
@@ -377,30 +403,42 @@ namespace Avalonia.Build.Tasks
                             classTypeDefinition.Fields.Add(designLoaderField);
 
                             const string TrampolineName = "!XamlIlPopulateTrampoline";
-                            var trampoline = new MethodDefinition(TrampolineName,
-                                MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
-                            trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
-                            classTypeDefinition.Methods.Add(trampoline);
+                            var trampolineMethodWithoutSP = new Lazy<MethodDefinition>(() => CreateTrampolineMethod(false));
+                            var trampolineMethodWithSP = new Lazy<MethodDefinition>(() => CreateTrampolineMethod(true));
+                            MethodDefinition CreateTrampolineMethod(bool hasSystemProviderArg)
+                            {
+                                var trampoline = new MethodDefinition(TrampolineName,
+                                    MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
+                                if (hasSystemProviderArg)
+                                {
+                                    trampoline.Parameters.Add(new ParameterDefinition(serviceProviderType));   
+                                }
+                                trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
+                                
+                                classTypeDefinition.Methods.Add(trampoline);
 
-                            var regularStart = Instruction.Create(OpCodes.Call, createRootServiceProviderMethod);
+                                var regularStart = Instruction.Create(OpCodes.Nop);
                             
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
+                                trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                             
-                            trampoline.Body.Instructions.Add(regularStart);
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
-                            trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
-                            CopyDebugDocument(trampoline, compiledPopulateMethod);
+                                trampoline.Body.Instructions.Add(regularStart);
+                                trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_0 : OpCodes.Ldnull));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod));
+                                trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); 
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
+                                trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
+                                CopyDebugDocument(trampoline, compiledPopulateMethod);
+                                return trampoline;
+                            }
 
                             var foundXamlLoader = false;
-                            // Find AvaloniaXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
-                            foreach (var method in classTypeDefinition.Methods
-                                .Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
+                            // Find AvaloniaXamlLoader.Load(this) or AvaloniaXamlLoader.Load(sp, this) and replace it with !XamlIlPopulateTrampoline(this)
+                            foreach (var method in classTypeDefinition.Methods.ToArray())
                             {
                                 var i = method.Body.Instructions;
                                 for (var c = 1; c < i.Count; c++)
@@ -422,7 +460,20 @@ namespace Avalonia.Build.Tasks
                                         {
                                             if (MatchThisCall(i, c - 1))
                                             {
-                                                i[c].Operand = trampoline;
+                                                i[c].Operand = trampolineMethodWithoutSP.Value;
+                                                foundXamlLoader = true;
+                                            }
+                                        }
+                                        if (op != null
+                                            && op.Name == "Load"
+                                            && op.Parameters.Count == 2
+                                            && op.Parameters[0].ParameterType.FullName == "System.IServiceProvider"
+                                            && op.Parameters[1].ParameterType.FullName == "System.Object"
+                                            && op.DeclaringType.FullName == "Avalonia.Markup.Xaml.AvaloniaXamlLoader")
+                                        {
+                                            if (MatchThisCall(i, c - 1))
+                                            {
+                                                i[c].Operand = trampolineMethodWithSP.Value;
                                                 foundXamlLoader = true;
                                             }
                                         }
@@ -439,7 +490,7 @@ namespace Avalonia.Build.Tasks
                                 {
                                     var i = ctors[0].Body.Instructions;
                                     var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
-                                    i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
+                                    i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampolineMethodWithoutSP.Value));
                                     i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
                                 }
                                 else
@@ -461,20 +512,33 @@ namespace Avalonia.Build.Tasks
                                 null :
                                 classTypeDefinition.GetConstructors().FirstOrDefault(c =>
                                     c.IsPublic && !c.IsStatic && !c.HasParameters);
+                            var constructorWithSp = compiledBuildMethod != null ?
+                                null :
+                                classTypeDefinition.GetConstructors().FirstOrDefault(c =>
+                                    c.IsPublic && !c.IsStatic && c.Parameters.Count == 1 && c.Parameters[0].ParameterType.FullName == serviceProviderType.FullName);
 
-                            if (compiledBuildMethod != null || parameterlessConstructor != null)
+                            if (compiledBuildMethod != null || parameterlessConstructor != null || constructorWithSp != null)
                             {
                                 var i = loaderDispatcherMethod.Body.Instructions;
                                 var nop = Instruction.Create(OpCodes.Nop);
-                                i.Add(Instruction.Create(OpCodes.Ldarg_0));
+                                i.Add(Instruction.Create(OpCodes.Ldarg_1));
                                 i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
                                 i.Add(Instruction.Create(OpCodes.Ldc_I4, (int)StringComparison.OrdinalIgnoreCase));
                                 i.Add(Instruction.Create(OpCodes.Call, stringEquals));
                                 i.Add(Instruction.Create(OpCodes.Brfalse, nop));
                                 if (parameterlessConstructor != null)
+                                {
                                     i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor));
+                                }
+                                else if (constructorWithSp != null)
+                                {
+                                    i.Add(Instruction.Create(OpCodes.Ldarg_0));
+                                    i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod));
+                                    i.Add(Instruction.Create(OpCodes.Newobj, constructorWithSp));
+                                }
                                 else
                                 {
+                                    i.Add(Instruction.Create(OpCodes.Ldarg_0));
                                     i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod));
                                     i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
                                 }
@@ -482,6 +546,11 @@ namespace Avalonia.Build.Tasks
                                 i.Add(Instruction.Create(OpCodes.Ret));
                                 i.Add(nop);
                             }
+                            else
+                            {
+                                engine.LogWarning(BuildEngineErrorCode.Loader, "",
+                                    $"XAML resource \"{res.Uri}\" won't be reachable via runtime loader, as no public constructor was found");
+                            }
                         }
 
                     }

+ 5 - 3
src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using Avalonia.Styling;
@@ -31,9 +32,10 @@ namespace Avalonia.Themes.Fluent
         /// <summary>
         /// Initializes a new instance of the <see cref="FluentTheme"/> class.
         /// </summary>
-        public FluentTheme()
+        /// <param name="sp">The parent's service provider.</param>
+        public FluentTheme(IServiceProvider? sp = null)
         {
-            AvaloniaXamlLoader.Load(this);
+            AvaloniaXamlLoader.Load(sp, this);
             
             _baseDark = (IResourceDictionary)GetAndRemove("BaseDark");
             _fluentDark = (IResourceDictionary)GetAndRemove("FluentDark");

+ 5 - 3
src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using Avalonia.Styling;
@@ -16,9 +17,10 @@ namespace Avalonia.Themes.Simple
         /// <summary>
         /// Initializes a new instance of the <see cref="SimpleTheme"/> class.
         /// </summary>
-        public SimpleTheme()
+        /// <param name="sp">The parent's service provider.</param>
+        public SimpleTheme(IServiceProvider? sp = null)
         {
-            AvaloniaXamlLoader.Load(this);
+            AvaloniaXamlLoader.Load(sp, this);
 
             _simpleDark = (IResourceDictionary)GetAndRemove("BaseDark");
             _simpleLight = (IResourceDictionary)GetAndRemove("BaseLight");

+ 2 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs

@@ -163,8 +163,9 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
         public bool NeedsParentStack => true;
         public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
         {
+            codeGen.Ldloc(context.ContextLocal);
             var method = context.GetAvaloniaTypes().RuntimeHelpers
-                .FindMethod(m => m.Name == "CreateRootServiceProviderV2");
+                .FindMethod(m => m.Name == "CreateRootServiceProviderV3");
             codeGen.EmitCall(method);
 
             return XamlILNodeEmitResult.Type(0, Type.GetClrType());

+ 12 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs

@@ -41,6 +41,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             public bool NeedsParentStack => true;
             public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
             {
+                if (_inheritContext)
+                {
+                    codeGen.Ldloc(context.ContextLocal);
+                }
+                else
+                {
+                    codeGen.Ldloc(context.ContextLocal);
+                    var method = context.GetAvaloniaTypes().RuntimeHelpers
+                        .FindMethod(m => m.Name == "CreateRootServiceProviderV3");
+                    codeGen.EmitCall(method);
+                }
+
                 codeGen.Ldloc(context.ContextLocal);
                 return XamlILNodeEmitResult.Type(0, Type.GetClrType());
             }

+ 39 - 2
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@@ -23,6 +23,17 @@ namespace Avalonia.Markup.Xaml
             throw new XamlLoadException(
                 $"No precompiled XAML found for {obj.GetType()}, make sure to specify x:Class and include your XAML file as AvaloniaResource");
         }
+        
+        /// <summary>
+        /// Loads the XAML into a Avalonia component.
+        /// </summary>
+        /// <param name="sp">The parent's service provider.</param>
+        /// <param name="obj">The object to load the XAML into.</param>
+        public static void Load(IServiceProvider? sp, object obj)
+        {
+            throw new XamlLoadException(
+                $"No precompiled XAML found for {obj.GetType()}, make sure to specify x:Class and include your XAML file as AvaloniaResource");
+        }
 
         /// <summary>
         /// Loads XAML from a URI.
@@ -33,6 +44,20 @@ namespace Avalonia.Markup.Xaml
         /// </param>
         /// <returns>The loaded object.</returns>
         public static object Load(Uri uri, Uri? baseUri = null)
+        {
+            return Load(null, uri, baseUri);
+        }
+        
+        /// <summary>
+        /// Loads XAML from a URI.
+        /// </summary>
+        /// <param name="sp">The parent's service provider.</param>
+        /// <param name="uri">The URI of the XAML file.</param>
+        /// <param name="baseUri">
+        /// A base URI to use if <paramref name="uri"/> is relative.
+        /// </param>
+        /// <returns>The loaded object.</returns>
+        public static object Load(IServiceProvider? sp, Uri uri, Uri? baseUri = null)
         {
             if (uri is null)
                 throw new ArgumentNullException(nameof(uri));
@@ -51,13 +76,25 @@ namespace Avalonia.Markup.Xaml
 
             var compiledLoader = assetLocator.GetAssembly(uri, baseUri)
                 ?.GetType("CompiledAvaloniaXaml.!XamlLoader")
-                ?.GetMethod("TryLoad", new[] {typeof(string)});
+                ?.GetMethod("TryLoad", new[] { typeof(System.IServiceProvider), typeof(string) });
             if (compiledLoader != null)
             {
-                var compiledResult = compiledLoader.Invoke(null, new object[] {absoluteUri.ToString()});
+                var compiledResult = compiledLoader.Invoke(null, new object?[] { sp, absoluteUri.ToString()});
                 if (compiledResult != null)
                     return compiledResult;
             }
+            else
+            {
+                compiledLoader = assetLocator.GetAssembly(uri, baseUri)
+                    ?.GetType("CompiledAvaloniaXaml.!XamlLoader")
+                    ?.GetMethod("TryLoad", new[] {typeof(string)});
+                if (compiledLoader != null)
+                {
+                    var compiledResult = compiledLoader.Invoke(null, new object?[] {absoluteUri.ToString()});
+                    if (compiledResult != null)
+                        return compiledResult;
+                }   
+            }
 
             // This is intended for unit-tests only
             var runtimeLoader = AvaloniaLocator.Current.GetService<IRuntimeXamlLoader>();

+ 2 - 19
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@@ -34,13 +34,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 _ => null,
             };
 
-            if (provideTarget.TargetObject is Setter setter)
+            if (provideTarget.TargetObject is Setter { Property: not null } setter)
             {
                 targetType = setter.Property.PropertyType;
             }
-
-            var previousWasControlTheme = false;
-
+            
             // Look upwards though the ambient context for IResourceNodes
             // which might be able to give us the resource.
             foreach (var parent in stack.Parents)
@@ -49,21 +47,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 {
                     return ColorToBrushConverter.Convert(value, targetType);
                 }
-
-                // HACK: Temporary fix for #8678. Hard-coded to only work for the DevTools main
-                // window as we don't want 3rd parties to start relying on this hack.
-                //
-                // We need to implement compile-time merging of resource dictionaries and this
-                // hack can be removed.
-                if (previousWasControlTheme &&
-                    parent is IResourceProvider hack &&
-                    hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" &&
-                    hack.Owner.TryGetResource(ResourceKey, out value))
-                {
-                    return ColorToBrushConverter.Convert(value, targetType);
-                }
-
-                previousWasControlTheme = parent is ControlTheme;
             }
 
             if (provideTarget.TargetObject is Control target &&

+ 3 - 1
src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs

@@ -10,6 +10,7 @@ namespace Avalonia.Markup.Xaml.Styling
     /// </summary>
     public class ResourceInclude : IResourceProvider
     {
+        private readonly IServiceProvider _serviceProvider;
         private readonly Uri? _baseUri;
         private IResourceDictionary? _loaded;
         private bool _isLoading;
@@ -29,6 +30,7 @@ namespace Avalonia.Markup.Xaml.Styling
         /// <param name="serviceProvider">The XAML service provider.</param>
         public ResourceInclude(IServiceProvider serviceProvider)
         {
+            _serviceProvider = serviceProvider;
             _baseUri = serviceProvider.GetContextBaseUri();
         }
 
@@ -43,7 +45,7 @@ namespace Avalonia.Markup.Xaml.Styling
                 {
                     _isLoading = true;
                     var source = Source ?? throw new InvalidOperationException("ResourceInclude.Source must be set.");
-                    _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(source, _baseUri);
+                    _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(_serviceProvider, source, _baseUri);
                     _isLoading = false;
                 }
 

+ 3 - 1
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@@ -12,6 +12,7 @@ namespace Avalonia.Markup.Xaml.Styling
     /// </summary>
     public class StyleInclude : IStyle, IResourceProvider
     {
+        private readonly IServiceProvider _serviceProvider;
         private readonly Uri? _baseUri;
         private IStyle[]? _loaded;
         private bool _isLoading;
@@ -31,6 +32,7 @@ namespace Avalonia.Markup.Xaml.Styling
         /// <param name="serviceProvider">The XAML service provider.</param>
         public StyleInclude(IServiceProvider serviceProvider)
         {
+            _serviceProvider = serviceProvider;
             _baseUri = serviceProvider.GetContextBaseUri();
         }
 
@@ -52,7 +54,7 @@ namespace Avalonia.Markup.Xaml.Styling
                 {
                     _isLoading = true;
                     var source = Source ?? throw new InvalidOperationException("StyleInclude.Source must be set.");
-                    var loaded = (IStyle)AvaloniaXamlLoader.Load(source, _baseUri);
+                    var loaded = (IStyle)AvaloniaXamlLoader.Load(_serviceProvider, source, _baseUri);
                     _loaded = new[] { loaded };
                     _isLoading = false;
                 }

+ 20 - 8
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@@ -167,18 +167,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
         #line hidden
         public static IServiceProvider CreateRootServiceProviderV2()
         {
-            return new RootServiceProvider(new NameScope());
+            return new RootServiceProvider(new NameScope(), null);
+        }
+        public static IServiceProvider CreateRootServiceProviderV3(IServiceProvider parentServiceProvider)
+        {
+            return new RootServiceProvider(new NameScope(), parentServiceProvider);
         }
         #line default
 
-        class RootServiceProvider : IServiceProvider, IAvaloniaXamlIlParentStackProvider
+        class RootServiceProvider : IServiceProvider
         {
             private readonly INameScope _nameScope;
+            private readonly IServiceProvider _parentServiceProvider;
             private readonly IRuntimePlatform _runtimePlatform;
 
-            public RootServiceProvider(INameScope nameScope)
+            public RootServiceProvider(INameScope nameScope, IServiceProvider parentServiceProvider)
             {
                 _nameScope = nameScope;
+                _parentServiceProvider = parentServiceProvider;
                 _runtimePlatform = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
             }
 
@@ -187,19 +193,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
                 if (serviceType == typeof(INameScope))
                     return _nameScope;
                 if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
-                    return this;
+                    return _parentServiceProvider?.GetService<IAvaloniaXamlIlParentStackProvider>()
+                           ?? DefaultAvaloniaXamlIlParentStackProvider.Instance;
                 if (serviceType == typeof(IRuntimePlatform))
                     return _runtimePlatform ?? throw new KeyNotFoundException($"{nameof(IRuntimePlatform)} was not registered");
 
                 return null;
             }
 
-            public IEnumerable<object> Parents
+            private class DefaultAvaloniaXamlIlParentStackProvider : IAvaloniaXamlIlParentStackProvider
             {
-                get
+                public static DefaultAvaloniaXamlIlParentStackProvider Instance { get; } = new(); 
+                
+                public IEnumerable<object> Parents
                 {
-                    if (Application.Current != null)
-                        yield return Application.Current;
+                    get
+                    {
+                        if (Application.Current != null)
+                            yield return Application.Current;
+                    }
                 }
             }
         }

+ 1 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@@ -30,6 +30,7 @@
     </AvaloniaResource>
     <AvaloniaResource Include="Xaml\XamlIlClassWithPrecompiledXaml.xaml" />
     <AvaloniaResource Include="Xaml\XamlIlClassWithCustomProperty.xaml" />
+    <AvaloniaResource Include="Xaml\StyleWithServiceLocator.xaml" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Avalonia.IntegrationTests.Appium\PlatformFactAttribute.cs">

+ 43 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

@@ -2,6 +2,10 @@
 using System.Collections.Generic;
 using System.Linq;
 using Avalonia.Controls;
+using Avalonia.Markup.Xaml.Styling;
+using Avalonia.Markup.Xaml.XamlIl.Runtime;
+using Avalonia.Media;
+using Avalonia.Platform;
 using Avalonia.Styling;
 using Avalonia.Themes.Simple;
 using Avalonia.UnitTests;
@@ -265,4 +269,43 @@ public class StyleIncludeTests
         Assert.IsType<SimpleTheme>(control.Styles[0]);
         Assert.IsType<SimpleTheme>(control.Styles[1]);
     }
+
+    [Fact]
+    public void StyleInclude_From_CodeBehind_Resolves_Compiled()
+    {
+        using var locatorScope = AvaloniaLocator.EnterScope();
+        AvaloniaLocator.CurrentMutable.BindToSelf<IAssetLoader>(new AssetLoader(GetType().Assembly));
+        
+        var sp = new TestServiceProvider();
+        var styleInclude = new StyleInclude(sp)
+        {
+            Source = new Uri("avares://Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml")
+        };
+
+        var loaded = Assert.IsType<StyleWithServiceLocator>(styleInclude.Loaded);
+        
+        Assert.Equal(
+            sp.GetService<IAvaloniaXamlIlParentStackProvider>().Parents, 
+            loaded.ServiceProvider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents);
+    }
+
+    private class TestServiceProvider : IServiceProvider, IUriContext, IAvaloniaXamlIlParentStackProvider
+    {
+        private IServiceProvider _root = XamlIlRuntimeHelpers.CreateRootServiceProviderV2();
+        public object GetService(Type serviceType)
+        {
+            if (serviceType == typeof(IUriContext))
+            {
+                return this;
+            }
+            if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
+            {
+                return this;
+            }
+            return _root.GetService(serviceType);
+        }
+
+        public Uri BaseUri { get; set; }
+        public IEnumerable<object> Parents { get; } = new[] { new ContentControl() };
+    }
 }

+ 5 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml

@@ -0,0 +1,5 @@
+<Style xmlns='https://github.com/avaloniaui'
+       xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+       x:Class='Avalonia.Markup.Xaml.UnitTests.Xaml.StyleWithServiceLocator'>
+    
+</Style>

+ 16 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs

@@ -0,0 +1,16 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Styling;
+
+namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
+
+public class StyleWithServiceLocator : Style
+{
+    public IServiceProvider ServiceProvider { get; }
+
+    public StyleWithServiceLocator(IServiceProvider sp = null)
+    {
+        ServiceProvider = sp;
+        AvaloniaXamlLoader.Load(sp, this);
+    }
+}