Browse Source

Add ServiceProvider to the xaml runtime loader too

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

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

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

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

@ -66,5 +66,10 @@ public class RuntimeXamlLoaderDocument
/// <summary>
/// The stream containing the XAML.
/// </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 Include="Xaml\XamlIlClassWithPrecompiledXaml.xaml" />
<AvaloniaResource Include="Xaml\XamlIlClassWithCustomProperty.xaml" />
<AvaloniaResource Include="Xaml\StyleWithServiceLocator.xaml" />
<AvaloniaResource Include="Xaml\StyleWithServiceProvider.xaml" />
</ItemGroup>
<ItemGroup>
<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 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(
sp.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();
public object GetService(Type serviceType)
if (serviceType == typeof(IUriContext))
{
if (serviceType == typeof(IUriContext))
{
return this;
}
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
{
return this;
}
return _root.GetService(serviceType);
return this;
}
public Uri BaseUri { get; set; }
public IEnumerable<object> Parents { get; } = new[] { new ContentControl() };
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
{
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'
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>

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;
public class StyleWithServiceLocator : Style
public class StyleWithServiceProvider : Style
{
public IServiceProvider ServiceProvider { get; }
public StyleWithServiceLocator(IServiceProvider sp = null)
public StyleWithServiceProvider(IServiceProvider sp = null)
{
ServiceProvider = sp;
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.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml.UnitTests.Xaml;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading;
@ -314,6 +315,26 @@ namespace Avalonia.Markup.Xaml.UnitTests
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

Loading…
Cancel
Save