зеркало из https://github.com/DeGsoft/maui-linux.git
[Xaml] allow the Previewer to provide their own Xaml files for any type (#262)
* [Xaml] allow the Previewer to provide their own Xaml files for any type * [Xaml] use a Func instead of an interface, easier to use by reflection. Add tests * [XamlC] move the InitializeComponent duplication to XamlC task * [XamlC] generate branching code * [XamlC] fix the XamlC issue * [XamlC] make the API public * [docs] fix docs
This commit is contained in:
Родитель
272033723e
Коммит
f304f25df2
|
@ -14,8 +14,8 @@
|
|||
<PropertyGroup>
|
||||
<CompileDependsOn>
|
||||
$(CompileDependsOn);
|
||||
GenerateDebugCode;
|
||||
XamlC;
|
||||
GenerateDebugCode;
|
||||
</CompileDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -67,18 +67,11 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
LogLine(2, "no InitializeComponent found... skipped.");
|
||||
continue;
|
||||
}
|
||||
if (typeDef.Methods.FirstOrDefault(md => md.Name == "InitCompRuntime") != null)
|
||||
{
|
||||
LogLine(2, "InitCompRuntime already exists... skipped");
|
||||
var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
|
||||
if (initCompRuntime == null) {
|
||||
LogLine(2, "no __InitComponentRuntime found... skipped.");
|
||||
continue;
|
||||
}
|
||||
LogLine(2, "");
|
||||
|
||||
LogString(2, " Duplicating {0}.InitializeComponent () into {0}.InitCompRuntime ... ", typeDef.Name);
|
||||
var initCompRuntime = new MethodDefinition("InitCompRuntime", initComp.Attributes, initComp.ReturnType);
|
||||
initCompRuntime.Body = initComp.Body;
|
||||
typeDef.Methods.Add(initCompRuntime);
|
||||
LogLine(2, "done.");
|
||||
|
||||
// IL_0000: ldarg.0
|
||||
// IL_0001: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.ContentPage::'.ctor'()
|
||||
|
@ -92,7 +85,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
// IL_0013: br IL_001e
|
||||
//
|
||||
// IL_0018: ldarg.0
|
||||
// IL_0019: callvirt instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::InitCompRuntime()
|
||||
// IL_0019: callvirt instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::__InitComponentRuntime()
|
||||
// IL_001e: ret
|
||||
|
||||
var altCtor =
|
||||
|
|
|
@ -232,6 +232,16 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
}
|
||||
LogLine(2, "");
|
||||
|
||||
if (typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime") != null) {
|
||||
LogLine(2, " __InitComponentRuntime already exists... not duplicating");
|
||||
} else {
|
||||
LogString(2, " Duplicating {0}.InitializeComponent () into {0}.__InitComponentRuntime ... ", typeDef.Name);
|
||||
var initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType);
|
||||
initCompRuntime.Body = initComp.Body;
|
||||
typeDef.Methods.Add(initCompRuntime);
|
||||
LogLine(2, "done.");
|
||||
}
|
||||
|
||||
LogString(2, " Parsing Xaml... ");
|
||||
var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
|
||||
if (rootnode == null)
|
||||
|
@ -249,6 +259,45 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
var body = new MethodBody(initComp);
|
||||
var il = body.GetILProcessor();
|
||||
il.Emit(OpCodes.Nop);
|
||||
|
||||
// Generating branching code for the Previewer
|
||||
// IL_0007: call class [mscorlib]System.Func`2<class [mscorlib]System.Type,string> class [Xamarin.Forms.Xaml.Internals]Xamarin.Forms.Xaml.XamlLoader::get_XamlFileProvider()
|
||||
// IL_000c: brfalse IL_0031
|
||||
// IL_0011: call class [mscorlib]System.Func`2<class [mscorlib]System.Type,string> class [Xamarin.Forms.Xaml.Internals]Xamarin.Forms.Xaml.XamlLoader::get_XamlFileProvider()
|
||||
// IL_0016: ldarg.0
|
||||
// IL_0017: call instance class [mscorlib]System.Type object::GetType()
|
||||
// IL_001c: callvirt instance !1 class [mscorlib]System.Func`2<class [mscorlib]System.Type, string>::Invoke(!0)
|
||||
// IL_0021: brfalse IL_0031
|
||||
// IL_0026: ldarg.0
|
||||
// IL_0027: call instance void class Xamarin.Forms.Xaml.UnitTests.XamlLoaderGetXamlForTypeTests::__InitComponentRuntime()
|
||||
// IL_002c: ret
|
||||
// IL_0031: nop
|
||||
|
||||
var nop = Instruction.Create(OpCodes.Nop);
|
||||
var getXamlFileProvider = body.Method.Module.Import(body.Method.Module.Import(typeof(Xamarin.Forms.Xaml.Internals.XamlLoader))
|
||||
.Resolve()
|
||||
.Properties.FirstOrDefault(pd => pd.Name == "XamlFileProvider")
|
||||
.GetMethod);
|
||||
il.Emit(OpCodes.Call, getXamlFileProvider);
|
||||
il.Emit(OpCodes.Brfalse, nop);
|
||||
il.Emit(OpCodes.Call, getXamlFileProvider);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
var getType = body.Method.Module.Import(body.Method.Module.Import(typeof(object))
|
||||
.Resolve()
|
||||
.Methods.FirstOrDefault(md => md.Name == "GetType"));
|
||||
il.Emit(OpCodes.Call, getType);
|
||||
var func = body.Method.Module.Import(body.Method.Module.Import(typeof(Func<Type, string>))
|
||||
.Resolve()
|
||||
.Methods.FirstOrDefault(md => md.Name == "Invoke"));
|
||||
func = func.ResolveGenericParameters(body.Method.Module.Import(typeof(Func<Type, string>)), body.Method.Module);
|
||||
il.Emit(OpCodes.Callvirt, func);
|
||||
il.Emit(OpCodes.Brfalse, nop);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
|
||||
il.Emit(OpCodes.Call, initCompRuntime);
|
||||
il.Emit(OpCodes.Ret);
|
||||
il.Append(nop);
|
||||
|
||||
var visitorContext = new ILContext(il, body);
|
||||
|
||||
rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
|
||||
|
|
|
@ -347,6 +347,9 @@
|
|||
<Compile Include="SharedResourceDictionary2.xaml.cs">
|
||||
<DependentUpon>SharedResourceDictionary2.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="XamlLoaderGetXamlForTypeTests.xaml.cs">
|
||||
<DependentUpon>XamlLoaderGetXamlForTypeTests.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
|
||||
|
@ -617,6 +620,9 @@
|
|||
<EmbeddedResource Include="SharedResourceDictionary2.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="XamlLoaderGetXamlForTypeTests.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
|
|
|
@ -3,6 +3,8 @@ using NUnit.Framework;
|
|||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
#pragma warning disable 0618 //retaining legacy call to obsolete code
|
||||
|
||||
[TestFixture]
|
||||
public class XamlLoaderCreateTests
|
||||
{
|
||||
|
@ -35,4 +37,5 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
Assert.NotNull (button);
|
||||
}
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.XamlLoaderGetXamlForTypeTests">
|
||||
<Button x:Name="Button"/>
|
||||
</ContentPage>
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public partial class XamlLoaderGetXamlForTypeTests : ContentPage
|
||||
{
|
||||
public XamlLoaderGetXamlForTypeTests()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public XamlLoaderGetXamlForTypeTests(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Xamarin.Forms.Xaml.Internals.XamlLoader.XamlFileProvider = null;
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void XamlContentIsReplaced(bool useCompiledXaml)
|
||||
{
|
||||
var layout = new XamlLoaderGetXamlForTypeTests(useCompiledXaml);
|
||||
Assert.That(layout.Content, Is.TypeOf<Button>());
|
||||
|
||||
Xamarin.Forms.Xaml.Internals.XamlLoader.XamlFileProvider = (t) => {
|
||||
if (t == typeof(XamlLoaderGetXamlForTypeTests))
|
||||
return @"
|
||||
<ContentPage xmlns=""http://xamarin.com/schemas/2014/forms""
|
||||
xmlns:x=""http://schemas.microsoft.com/winfx/2009/xaml""
|
||||
x:Class=""Xamarin.Forms.Xaml.UnitTests.XamlLoaderGetXamlForTypeTests"">
|
||||
<Label x:Name=""Label""/>
|
||||
</ContentPage>";
|
||||
return null;
|
||||
};
|
||||
|
||||
layout = new XamlLoaderGetXamlForTypeTests(useCompiledXaml);
|
||||
Assert.That(layout.Content, Is.TypeOf<Label>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,15 @@ using System.Reflection;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.Internals
|
||||
{
|
||||
public static class XamlLoader
|
||||
{
|
||||
public static Func<Type, string> XamlFileProvider { get; internal set; }
|
||||
internal static bool DoNotThrowOnExceptions { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
internal static class XamlLoader
|
||||
|
@ -64,12 +73,16 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
|
||||
XamlParser.ParseXaml (rootnode, reader);
|
||||
Visit (rootnode, new HydratationContext { RootElement = view });
|
||||
Visit (rootnode, new HydratationContext {
|
||||
RootElement = view,
|
||||
DoNotThrowOnExceptions = Xamarin.Forms.Xaml.Internals.XamlLoader.DoNotThrowOnExceptions
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete ("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
|
||||
public static object Create (string xaml, bool doNotThrow = false)
|
||||
{
|
||||
object inflatedView = null;
|
||||
|
@ -113,6 +126,12 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
static string GetXamlForType(Type type)
|
||||
{
|
||||
string xaml = null;
|
||||
|
||||
//the Previewer might want to provide it's own xaml for this... let them do that
|
||||
if (Xamarin.Forms.Xaml.Internals.XamlLoader.XamlFileProvider != null && (xaml = Xamarin.Forms.Xaml.Internals.XamlLoader.XamlFileProvider(type)) != null)
|
||||
return xaml;
|
||||
|
||||
var assembly = type.GetTypeInfo().Assembly;
|
||||
|
||||
string resourceId;
|
||||
|
@ -129,7 +148,6 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
// first pass, pray to find it because the user named it correctly
|
||||
|
||||
string xaml = null;
|
||||
foreach (var resource in resourceNames)
|
||||
{
|
||||
if (ResourceMatchesFilename(assembly, resource, likelyResourceName))
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<Type Name="XamlLoader" FullName="Xamarin.Forms.Xaml.Internals.XamlLoader">
|
||||
<TypeSignature Language="C#" Value="public static class XamlLoader" />
|
||||
<TypeSignature Language="ILAsm" Value=".class public auto ansi abstract sealed beforefieldinit XamlLoader extends System.Object" />
|
||||
<AssemblyInfo>
|
||||
<AssemblyName>Xamarin.Forms.Xaml</AssemblyName>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<Base>
|
||||
<BaseTypeName>System.Object</BaseTypeName>
|
||||
</Base>
|
||||
<Interfaces />
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
<Members>
|
||||
<Member MemberName="XamlFileProvider">
|
||||
<MemberSignature Language="C#" Value="public static Func<Type,string> XamlFileProvider { get; }" />
|
||||
<MemberSignature Language="ILAsm" Value=".property class System.Func`2<class System.Type, string> XamlFileProvider" />
|
||||
<MemberType>Property</MemberType>
|
||||
<AssemblyInfo>
|
||||
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||
</AssemblyInfo>
|
||||
<ReturnValue>
|
||||
<ReturnType>System.Func<System.Type,System.String></ReturnType>
|
||||
</ReturnValue>
|
||||
<Docs>
|
||||
<summary>To be added.</summary>
|
||||
<value>To be added.</value>
|
||||
<remarks>To be added.</remarks>
|
||||
</Docs>
|
||||
</Member>
|
||||
</Members>
|
||||
</Type>
|
|
@ -75,6 +75,7 @@
|
|||
<Namespace Name="Xamarin.Forms.Xaml.Internals">
|
||||
<Type Name="NameScopeProvider" Kind="Class" />
|
||||
<Type Name="SimpleValueTargetProvider" Kind="Class" />
|
||||
<Type Name="XamlLoader" Kind="Class" />
|
||||
<Type Name="XamlServiceProvider" Kind="Class" />
|
||||
<Type Name="XamlTypeResolver" Kind="Class" />
|
||||
<Type Name="XmlLineInfoProvider" Kind="Class" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче