Browse Source

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

pull/9541/head
Max Katz 3 years ago
parent
commit
de9e9580c0
  1. 1
      src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
  2. 119
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  3. 8
      src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
  4. 8
      src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs
  5. 3
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
  6. 12
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
  7. 41
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  8. 21
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  9. 4
      src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
  10. 4
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  11. 28
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  12. 1
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  13. 43
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
  14. 5
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml
  15. 16
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs

1
src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs

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

119
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");
}
}
}

8
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");

8
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");

3
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
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());
}

41
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>();

21
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 &&

4
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;
}

4
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;
}

28
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
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
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
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
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);
}
}
Loading…
Cancel
Save