Browse Source

Add ServiceProvider to the xaml runtime loader too

pull/9541/head
Max Katz 4 years ago
parent
commit
6ed60610c9
  1. 20
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
  2. 7
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  3. 7
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
  4. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  5. 35
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
  6. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceProvider.xaml
  7. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceProvider.xaml.cs
  8. 21
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

20
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs

@ -209,7 +209,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
}; };
var parsedDocuments = new List<XamlDocumentResource>(); var parsedDocuments = new List<XamlDocumentResource>();
var rootInstances = new List<object>(); var originalDocuments = new List<RuntimeXamlLoaderDocument>();
foreach (var document in documents) foreach (var document in documents)
{ {
@ -235,7 +235,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
builder, builder,
compiler.DefinePopulateMethod(builder, parsed, AvaloniaXamlIlCompiler.PopulateName, true), compiler.DefinePopulateMethod(builder, parsed, AvaloniaXamlIlCompiler.PopulateName, true),
compiler.DefineBuildMethod(builder, parsed, AvaloniaXamlIlCompiler.BuildName, true))); compiler.DefineBuildMethod(builder, parsed, AvaloniaXamlIlCompiler.BuildName, true)));
rootInstances.Add(document.RootInstance); originalDocuments.Add(document);
} }
compiler.TransformGroup(parsedDocuments); compiler.TransformGroup(parsedDocuments);
@ -251,7 +251,9 @@ namespace Avalonia.Markup.Xaml.XamlIl
indexerClosureType.CreateTypeInfo(); indexerClosureType.CreateTypeInfo();
trampolineBuilder.CreateTypeInfo(); trampolineBuilder.CreateTypeInfo();
return createdTypes.Zip(rootInstances, (l, r) => (l, r)).Select(t => LoadOrPopulate(t.Item1, t.Item2)).ToArray(); return createdTypes.Zip(originalDocuments, (l, r) => (l, r))
.Select(t => LoadOrPopulate(t.Item1, t.Item2.RootInstance, t.Item2.ServiceProvider))
.ToArray();
} }
static object LoadSreCore(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration) static object LoadSreCore(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration)
@ -260,7 +262,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
} }
#endif #endif
static object LoadOrPopulate(Type created, object rootInstance) static object LoadOrPopulate(Type created, object rootInstance, IServiceProvider parentServiceProvider)
{ {
var isp = Expression.Parameter(typeof(IServiceProvider)); var isp = Expression.Parameter(typeof(IServiceProvider));
@ -271,6 +273,8 @@ namespace Avalonia.Markup.Xaml.XamlIl
var populateCb = Expression.Lambda<Action<IServiceProvider, object>>( var populateCb = Expression.Lambda<Action<IServiceProvider, object>>(
Expression.Call(populate, isp, Expression.Convert(epar, populate.GetParameters()[1].ParameterType)), Expression.Call(populate, isp, Expression.Convert(epar, populate.GetParameters()[1].ParameterType)),
isp, epar).Compile(); isp, epar).Compile();
var serviceProvider = XamlIlRuntimeHelpers.CreateRootServiceProviderV3(parentServiceProvider);
if (rootInstance == null) if (rootInstance == null)
{ {
@ -282,7 +286,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
{ {
overrideField.SetValue(null, overrideField.SetValue(null,
new Action<object>( new Action<object>(
target => { populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), target); })); target => { populateCb(serviceProvider, target); }));
try try
{ {
return Activator.CreateInstance(targetType); return Activator.CreateInstance(targetType);
@ -296,11 +300,11 @@ namespace Avalonia.Markup.Xaml.XamlIl
var createCb = Expression.Lambda<Func<IServiceProvider, object>>( var createCb = Expression.Lambda<Func<IServiceProvider, object>>(
Expression.Convert(Expression.Call( Expression.Convert(Expression.Call(
created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile(); created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile();
return createCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2()); return createCb(serviceProvider);
} }
else else
{ {
populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), rootInstance); populateCb(serviceProvider, rootInstance);
return rootInstance; return rootInstance;
} }
} }
@ -411,7 +415,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
var loaded = Assembly.LoadFile(asmPath) var loaded = Assembly.LoadFile(asmPath)
.GetTypes().First(x => x.Name == safeUri); .GetTypes().First(x => x.Name == safeUri);
_cecilGeneratedCache[safeUri] = loaded; _cecilGeneratedCache[safeUri] = loaded;
return LoadOrPopulate(loaded, rootInstance); return LoadOrPopulate(loaded, rootInstance, null);
} }
#endif #endif
} }

7
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -103,10 +103,9 @@ namespace Avalonia.Markup.Xaml
var asset = assetLocator.OpenAndGetAssembly(uri, baseUri); var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
using (var stream = asset.stream) using (var stream = asset.stream)
{ {
return runtimeLoader.Load(new RuntimeXamlLoaderDocument(absoluteUri, stream), new RuntimeXamlLoaderConfiguration var document = new RuntimeXamlLoaderDocument(absoluteUri, stream) { ServiceProvider = sp };
{ var configuration = new RuntimeXamlLoaderConfiguration { LocalAssembly = asset.assembly };
LocalAssembly = asset.assembly return runtimeLoader.Load(document, configuration);
});
} }
} }

7
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs

@ -66,5 +66,10 @@ public class RuntimeXamlLoaderDocument
/// <summary> /// <summary>
/// The stream containing the XAML. /// The stream containing the XAML.
/// </summary> /// </summary>
public Stream XamlStream { get; set; } public Stream XamlStream { get; }
/// <summary>
/// Parent's service provider to pass to the Build method or type ctor, if available.
/// </summary>
public IServiceProvider? ServiceProvider { get; set; }
} }

2
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

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

35
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

@ -279,33 +279,34 @@ public class StyleIncludeTests
var sp = new TestServiceProvider(); var sp = new TestServiceProvider();
var styleInclude = new StyleInclude(sp) var styleInclude = new StyleInclude(sp)
{ {
Source = new Uri("avares://Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml") Source = new Uri("avares://Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceProvider.xaml")
}; };
var loaded = Assert.IsType<StyleWithServiceLocator>(styleInclude.Loaded); var loaded = Assert.IsType<StyleWithServiceProvider>(styleInclude.Loaded);
Assert.Equal( Assert.Equal(
sp.GetService<IAvaloniaXamlIlParentStackProvider>().Parents, sp.GetService<IAvaloniaXamlIlParentStackProvider>().Parents,
loaded.ServiceProvider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents); loaded.ServiceProvider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents);
} }
}
private class TestServiceProvider : IServiceProvider, IUriContext, IAvaloniaXamlIlParentStackProvider public class TestServiceProvider : IServiceProvider, IUriContext, IAvaloniaXamlIlParentStackProvider
{
private IServiceProvider _root = XamlIlRuntimeHelpers.CreateRootServiceProviderV2();
public object GetService(Type serviceType)
{ {
private IServiceProvider _root = XamlIlRuntimeHelpers.CreateRootServiceProviderV2(); if (serviceType == typeof(IUriContext))
public object GetService(Type serviceType)
{ {
if (serviceType == typeof(IUriContext)) return this;
{
return this;
}
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
{
return this;
}
return _root.GetService(serviceType);
} }
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
public Uri BaseUri { get; set; } {
public IEnumerable<object> Parents { get; } = new[] { new ContentControl() }; return this;
}
return _root.GetService(serviceType);
} }
public Uri BaseUri { get; set; }
public List<object> Parents { get; set; } = new List<object> { new ContentControl() };
IEnumerable<object> IAvaloniaXamlIlParentStackProvider.Parents => Parents;
} }

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml → tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceProvider.xaml

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

4
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs → tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceProvider.xaml.cs

@ -4,11 +4,11 @@ using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml; namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class StyleWithServiceLocator : Style public class StyleWithServiceProvider : Style
{ {
public IServiceProvider ServiceProvider { get; } public IServiceProvider ServiceProvider { get; }
public StyleWithServiceLocator(IServiceProvider sp = null) public StyleWithServiceProvider(IServiceProvider sp = null)
{ {
ServiceProvider = sp; ServiceProvider = sp;
AvaloniaXamlLoader.Load(sp, this); AvaloniaXamlLoader.Load(sp, this);

21
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

@ -9,6 +9,7 @@ using Avalonia.Data.Converters;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml.UnitTests.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
@ -314,6 +315,26 @@ namespace Avalonia.Markup.Xaml.UnitTests
Assert.NotNull(parsed.ItemTemplate); Assert.NotNull(parsed.ItemTemplate);
} }
[Fact]
public void Runtime_Loader_Should_Pass_Parents_From_ServiceProvider()
{
var sp = new TestServiceProvider
{
Parents = new List<object>
{
new UserControl { Resources = { ["Resource1"] = new SolidColorBrush(Colors.Blue) } }
}
};
var document = new RuntimeXamlLoaderDocument(@"
<Button xmlns='https://github.com/avaloniaui' Background='{StaticResource Resource1}' />")
{
ServiceProvider = sp
};
var parsed = (Button)AvaloniaRuntimeXamlLoader.Load(document);
Assert.Equal(Colors.Blue, ((ISolidColorBrush)parsed.Background!).Color);
}
} }
public class XamlIlBugTestsEventHandlerCodeBehind : Window public class XamlIlBugTestsEventHandlerCodeBehind : Window

Loading…
Cancel
Save