This commit is contained in:
Rui Marinho 2018-12-04 23:04:14 +00:00
Родитель 19ac6e7b1f 2ce7edfd01
Коммит 0a67f8b0a1
40 изменённых файлов: 376 добавлений и 263 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -59,5 +59,7 @@ AndroidNative/.gradle/
AndroidNative/gradlew
AndroidNative/gradlew.bat
!EmbeddingTestBeds/Embedding.iOS/*.designer.cs
*.nupkg
*.pdb
# temporary vim files
*.swp

Просмотреть файл

@ -26,6 +26,7 @@
<dependency id="Xamarin.Android.Support.v7.AppCompat" version="28.0.0-preview8"/>
<dependency id="Xamarin.Android.Support.v7.CardView" version="28.0.0-preview8"/>
<dependency id="Xamarin.Android.Support.v7.MediaRouter" version="28.0.0-preview8"/>
<dependency id="Xamarin.Android.Support.CustomTabs" version="28.0.0-preview8"/>
</group>
<group targetFramework="uap10.0">
<dependency id="NETStandard.Library" version="2.0.1"/>

Просмотреть файл

@ -36,7 +36,7 @@ namespace XFCorePostProcessor.Tasks
var module = assemblyDefinition.MainModule;
if(resourceLoader.GetMethods().Count(md=>md.Name == "get_ResourceProvider") > 1) {
Log.LogMessage(" already executed");
return false;
return true;
}
var methodDef = new MethodDefinition("get_ResourceProvider",
MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичные данные
XFCorePostProcessor.Tasks/bin/Debug/net461/System.IO.Compression.dll Executable file → Normal file

Двоичный файл не отображается.

Двоичные данные
XFCorePostProcessor.Tasks/bin/Debug/net461/System.Runtime.InteropServices.RuntimeInformation.dll Executable file → Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичные данные
Xamarin.Forms.9.9.9.nupkg

Двоичный файл не отображается.

Двоичные данные
Xamarin.Forms.AppLinks.9.9.9.nupkg

Двоичный файл не отображается.

Просмотреть файл

@ -51,5 +51,16 @@ namespace Xamarin.Forms.Build.Tasks
var t = ((GenericInstanceType)declaringTypeRef).GenericArguments[((GenericParameter)self.ReturnType).Position];
return t;
}
public static bool HasCustomAttributes(this MethodDefinition self, TypeReference attribute)
{
if (!self.HasCustomAttributes)
return false;
foreach (var arg in self.CustomAttributes) {
if (TypeRefComparer.Default.Equals(arg.AttributeType, attribute))
return true;
}
return false;
}
}
}

Просмотреть файл

@ -214,8 +214,7 @@ namespace Xamarin.Forms.Build.Tasks
public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
{
name = default(XmlName);
var parentElement = parentNode as IElementNode;
if (parentElement == null)
if (!(parentNode is IElementNode parentElement))
return false;
foreach (var kvp in parentElement.Properties)
{
@ -229,8 +228,7 @@ namespace Xamarin.Forms.Build.Tasks
static bool IsCollectionItem(INode node, INode parentNode)
{
var parentList = parentNode as IListNode;
if (parentList == null)
if (!(parentNode is IListNode parentList))
return false;
return parentList.CollectionItems.Contains(node);
}
@ -360,14 +358,12 @@ namespace Xamarin.Forms.Build.Tasks
//TODO support casting operators
var module = context.Module;
INode pathNode;
if (!node.Properties.TryGetValue(new XmlName("", "Path"), out pathNode) && node.CollectionItems.Any())
pathNode = node.CollectionItems [0];
if (!node.Properties.TryGetValue(new XmlName("", "Path"), out INode pathNode) && node.CollectionItems.Any())
pathNode = node.CollectionItems[0];
var path = (pathNode as ValueNode)?.Value as string;
BindingMode declaredmode;
if ( !node.Properties.TryGetValue(new XmlName("", "Mode"), out INode modeNode)
|| !Enum.TryParse((modeNode as ValueNode)?.Value as string, true, out declaredmode))
declaredmode = BindingMode.TwoWay; //meaning the mode isn't specified in the Binding extension. generate getters, setters, handlers
|| !Enum.TryParse((modeNode as ValueNode)?.Value as string, true, out BindingMode declaredmode))
declaredmode = BindingMode.TwoWay; //meaning the mode isn't specified in the Binding extension. generate getters, setters, handlers
INode dataTypeNode = null;
IElementNode n = node;
@ -376,8 +372,7 @@ namespace Xamarin.Forms.Build.Tasks
break;
n = n.Parent as IElementNode;
}
var dataType = (dataTypeNode as ValueNode)?.Value as string;
if (dataType == null)
if (!((dataTypeNode as ValueNode)?.Value is string dataType))
yield break; //throw
var prefix = dataType.Contains(":") ? dataType.Substring(0, dataType.IndexOf(":", StringComparison.Ordinal)) : "";
@ -398,17 +393,22 @@ namespace Xamarin.Forms.Build.Tasks
tPropertyRef = lastProp.property.ResolveGenericPropertyType(lastProp.propDeclTypeRef, module);
}
tPropertyRef = module.ImportReference(tPropertyRef);
var funcRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
var valuetupleRef = context.Module.ImportReference(module.ImportReference(("mscorlib", "System", "ValueTuple`2")).MakeGenericInstanceType(new[] { tPropertyRef, module.TypeSystem.Boolean }));
var funcRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, valuetupleRef }));
var actionRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Action`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
var funcObjRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
var tupleRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String}));
var typedBindingRef = module.ImportReference(module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "TypedBinding`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef}));
var ctorInfo = module.ImportReference(typedBindingRef.ResolveCached().Methods.FirstOrDefault(md => md.IsConstructor && !md.IsStatic && md.Parameters.Count == 3 ));
//FIXME: make sure the non-deprecated one is used
var ctorInfo = module.ImportReference(typedBindingRef.ResolveCached().Methods.FirstOrDefault(md =>
md.IsConstructor
&& !md.IsStatic
&& md.Parameters.Count == 3
&& !md.HasCustomAttributes (module.ImportReference(("mscorlib", "System", "ObsoleteAttribute")))));
var ctorinforef = ctorInfo.MakeGeneric(typedBindingRef, funcRef, actionRef, tupleRef);
yield return Instruction.Create(OpCodes.Ldloc, bindingExt);
yield return Create(Ldloc, bindingExt);
foreach (var instruction in CompiledBindingGetGetter(tSourceRef, tPropertyRef, properties, node, context))
yield return instruction;
if (declaredmode != BindingMode.OneTime && declaredmode != BindingMode.OneWay) { //if the mode is explicitly 1w, or 1t, no need for setters
@ -421,8 +421,8 @@ namespace Xamarin.Forms.Build.Tasks
yield return instruction;
} else
yield return Create(Ldnull);
yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(ctorinforef));
yield return Instruction.Create(OpCodes.Callvirt, module.ImportPropertySetterReference(("Xamarin.Forms.Xaml", "Xamarin.Forms.Xaml", "BindingExtension"), propertyName: "TypedBinding"));
yield return Create(Newobj, module.ImportReference(ctorinforef));
yield return Create(Callvirt, module.ImportPropertySetterReference(("Xamarin.Forms.Xaml", "Xamarin.Forms.Xaml", "BindingExtension"), propertyName: "TypedBinding"));
}
static IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> ParsePath(string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
@ -476,32 +476,44 @@ namespace Xamarin.Forms.Build.Tasks
static IEnumerable<Instruction> CompiledBindingGetGetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
{
// .method private static hidebysig default string '<Main>m__0' (class ViewModel vm) cil managed
// {
// .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ...
//
// IL_0000: ldarg.0
// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
// IL_0006: callvirt instance string class ViewModel::get_Text()
// IL_0006: ret
// }
// .method private static hidebysig default valuetype[mscorlib] System.ValueTuple`2<string, bool> '<Main>m__0' (class ViewModel A_0) cil managed
// {
// .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
// IL_0000: ldarg.0
// IL_0001: dup
// IL_0002: ldnull
// IL_0003: ceq
// IL_0005: brfalse IL_0013
// IL_000a: pop
// IL_000b: ldnull
// IL_000c: ldc.i4.0
// IL_000d: newobj instance void valuetype[mscorlib]System.ValueTuple`2<string, bool>::'.ctor'(!0, !1)
// IL_0012: ret
// IL_0013: nop
// IL_0014: call instance string class ViewModel::get_Text()
// IL_0019: ldc.i4.1
// IL_001a: newobj instance void valuetype[mscorlib]System.ValueTuple`2<string, bool>::'.ctor'(!0, !1)
// IL_001f: ret
// }
var module = context.Module;
var tupleRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "ValueTuple`2")).MakeGenericInstanceType(new[] { tPropertyRef, module.TypeSystem.Boolean }));
var tupleCtorRef = module.ImportCtorReference(tupleRef, 2);
tupleCtorRef = module.ImportReference(tupleCtorRef.ResolveGenericParameters(tupleRef, module));
var getter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
tPropertyRef) {
Parameters = {
new ParameterDefinition(tSourceRef)
},
CustomAttributes = {
new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
}
tupleRef) {
Parameters = { new ParameterDefinition(tSourceRef) },
CustomAttributes = { new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null)) }
};
getter.Body.InitLocals = true;
var il = getter.Body.GetILProcessor();
if (properties == null || properties.Count == 0) { //return self
il.Emit(Ldarg_0);
il.Emit(Ldc_I4_1); //true
il.Emit(Newobj, tupleCtorRef);
il.Emit(Ret);
}
else {
@ -510,39 +522,60 @@ namespace Xamarin.Forms.Build.Tasks
else
il.Emit(Ldarg_0);
foreach (var propTuple in properties) {
var property = propTuple.property;
var propDeclType = propTuple.propDeclTypeRef;
var indexerArg = propTuple.indexArg;
if (indexerArg != null) {
if (property.GetMethod.Parameters[0].ParameterType == module.TypeSystem.String)
il.Emit(Ldstr, indexerArg);
else if (property.GetMethod.Parameters[0].ParameterType == module.TypeSystem.Int32) {
if (!int.TryParse(indexerArg, out int index))
throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
il.Emit(Ldc_I4, index);
for (int i = 0; i < properties.Count; i++) {
(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg) = properties[i];
if (!property.PropertyType.IsValueType) { //if part of the path is null, return (default(T), false)
var nop = Create(Nop);
il.Emit(Dup);
il.Emit(Ldnull);
il.Emit(Ceq);
il.Emit(Brfalse, nop);
il.Emit(Pop);
if (tPropertyRef.IsValueType) {
var defaultValueVarDef = new VariableDefinition(tPropertyRef);
getter.Body.Variables.Add(defaultValueVarDef);
il.Emit(Ldloca_S, defaultValueVarDef);
il.Emit(Initobj, tPropertyRef);
il.Emit(Ldloc, defaultValueVarDef);
}
else
il.Emit(Ldnull);
il.Emit(Ldc_I4_0); //false
il.Emit(Newobj, tupleCtorRef);
il.Emit(Ret);
il.Append(nop);
}
var getMethod = module.ImportReference(property.GetMethod);
getMethod = module.ImportReference(getMethod.ResolveGenericParameters(propDeclType, module));
if (indexArg != null) {
if (property.GetMethod.Parameters[0].ParameterType == module.TypeSystem.String)
il.Emit(Ldstr, indexArg);
else if (property.GetMethod.Parameters[0].ParameterType == module.TypeSystem.Int32 && int.TryParse(indexArg, out int index))
il.Emit(Ldc_I4, index);
else
throw new XamlParseException($"Binding: {indexArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
}
var getMethod = module.ImportReference((module.ImportReference(property.GetMethod)).ResolveGenericParameters(propDeclTypeRef, module));
if (property.GetMethod.IsVirtual)
il.Emit(Callvirt, getMethod);
else
il.Emit(Call, getMethod);
}
}
il.Emit(Ldc_I4_1); //true
il.Emit(Newobj, tupleCtorRef);
il.Emit(Ret);
}
context.Body.Method.DeclaringType.Methods.Add(getter);
// IL_0007: ldnull
// IL_0008: ldftn string class Test::'<Main>m__0'(class ViewModel)
// IL_000e: newobj instance void class [mscorlib]System.Func`2<class ViewModel, string>::'.ctor'(object, native int)
// IL_02fa: ldnull
// IL_02fb: ldftn valuetype[mscorlib]System.ValueTuple`2 <string,bool> class Test::'<Main>m__0'(class ViewModel)
// IL_0301: newobj instance void class [mscorlib] System.Func`2<class ViewModel, valuetype[mscorlib] System.ValueTuple`2<string, bool>>::'.ctor'(object, native int)
yield return Create(Ldnull);
yield return Create(Ldftn, getter);
yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Func`2"), paramCount: 2, classArguments: new[] { tSourceRef, tPropertyRef }));
yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Func`2"), paramCount: 2, classArguments: new[] { tSourceRef, tupleRef }));
}
static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
@ -552,16 +585,16 @@ namespace Xamarin.Forms.Build.Tasks
yield break;
}
// .method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s) cil managed
// {
// .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
//
// IL_0000: ldarg.0
// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
// IL_0006: ldarg.1
// IL_0007: callvirt instance void class ViewModel::set_Text(string)
// IL_000c: ret
// }
// .method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s) cil managed
// {
// .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
//
// IL_0000: ldarg.0
// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
// IL_0006: ldarg.1
// IL_0007: callvirt instance void class ViewModel::set_Text(string)
// IL_000c: ret
// }
var module = context.Module;
var setter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",

Просмотреть файл

@ -48,14 +48,14 @@ namespace Xamarin.Forms.Build.Tasks
var resolver = DefaultAssemblyResolver ?? new XamlCAssemblyResolver();
if (resolver is XamlCAssemblyResolver xamlCResolver) {
if (!string.IsNullOrEmpty(DependencyPaths)) {
foreach (var dep in DependencyPaths.Split(';')) {
foreach (var dep in DependencyPaths.Split(';').Distinct()) {
LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {dep}");
xamlCResolver.AddSearchDirectory(dep);
}
}
if (!string.IsNullOrEmpty(ReferencePath)) {
var paths = ReferencePath.Replace("//", "/").Split(';');
var paths = ReferencePath.Replace("//", "/").Split(';').Distinct();
foreach (var p in paths) {
var searchpath = Path.GetDirectoryName(p);
LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {searchpath}");

Просмотреть файл

@ -385,20 +385,20 @@ namespace Xamarin.Forms.Build.Tasks
_xmlnsDefinitions = new List<XmlnsDefinitionAttribute>();
_xmlnsModules = new Dictionary<string, ModuleDefinition>();
if (string.IsNullOrEmpty(this.References))
if (string.IsNullOrEmpty(References))
return;
string[] paths = this.References.Split(';');
string[] paths = References.Split(';').Distinct().ToArray();
foreach ( var path in paths ) {
foreach (var path in paths) {
string asmName = Path.GetFileName(path);
if ( AssemblyIsSystem(asmName) )
if (AssemblyIsSystem(asmName))
// Skip the myriad "System." assemblies and others
continue;
using (var asmDef = AssemblyDefinition.ReadAssembly(path)) {
foreach ( var ca in asmDef.CustomAttributes ) {
if ( ca.AttributeType.FullName == typeof(XmlnsDefinitionAttribute).FullName) {
foreach (var ca in asmDef.CustomAttributes) {
if (ca.AttributeType.FullName == typeof(XmlnsDefinitionAttribute).FullName) {
_xmlnsDefinitions.Add(ca.GetXmlnsDefinition(asmDef));
_xmlnsModules[asmDef.FullName] = asmDef.MainModule;
}

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using NUnit.Framework;
using Xamarin.Forms.Internals;
@ -76,15 +77,26 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.AreEqual(shellItem, shell.CurrentItem);
}
ShellSection MakeSimpleShellSection (string route, string contentRoute)
ShellSection MakeSimpleShellSection(string route, string contentRoute)
{
return MakeSimpleShellSection(route, contentRoute, new ShellTestPage());
}
ShellSection MakeSimpleShellSection (string route, string contentRoute, ContentPage contentPage)
{
var shellSection = new ShellSection();
shellSection.Route = route;
var shellContent = new ShellContent { Content = new ContentPage(), Route = contentRoute };
var shellContent = new ShellContent { Content = contentPage, Route = contentRoute };
shellSection.Items.Add(shellContent);
return shellSection;
}
[QueryProperty("SomeQueryParameter", "SomeQueryParameter")]
public class ShellTestPage : ContentPage
{
public string SomeQueryParameter { get; set; }
}
[Test]
public void SimpleGoTo()
{
@ -115,6 +127,65 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tabfour/content/"));
}
[Test]
public async Task NavigationWithQueryStringWhenPageMatchesBindingContext()
{
var shell = new Shell();
shell.Route = "s";
var one = new ShellItem { Route = "one" };
var two = new ShellItem { Route = "two" };
var tabone = MakeSimpleShellSection("tabone", "content");
var tabfour = MakeSimpleShellSection("tabfour", "content", null);
one.Items.Add(tabone);
two.Items.Add(tabfour);
shell.Items.Add(one);
shell.Items.Add(two);
ShellTestPage pagetoTest = new ShellTestPage();
await shell.GoToAsync(new ShellNavigationState($"app:///s/two/tabfour/content?{nameof(ShellTestPage.SomeQueryParameter)}=1234"));
two.CurrentItem.CurrentItem.ContentTemplate = new DataTemplate(() =>
{
pagetoTest = new ShellTestPage();
pagetoTest.BindingContext = pagetoTest;
return pagetoTest;
});
var page = (two.CurrentItem.CurrentItem as IShellContentController).GetOrCreateContent();
Assert.AreEqual("1234", (page as ShellTestPage).SomeQueryParameter);
}
[Test]
public async Task NavigationWithQueryStringAndNoDataTemplate()
{
var shell = new Shell();
shell.Route = "s";
var one = new ShellItem { Route = "one" };
var two = new ShellItem { Route = "two" };
var tabone = MakeSimpleShellSection("tabone", "content");
var tabfour = MakeSimpleShellSection("tabfour", "content");
one.Items.Add(tabone);
two.Items.Add(tabfour);
shell.Items.Add(one);
shell.Items.Add(two);
await shell.GoToAsync(new ShellNavigationState($"app:///s/two/tabfour/content?{nameof(ShellTestPage.SomeQueryParameter)}=1234"));
Assert.AreEqual("1234", (two.CurrentItem.CurrentItem.Content as ShellTestPage).SomeQueryParameter);
}
[Test]
public void CancelNavigation()
{

Просмотреть файл

@ -51,7 +51,7 @@ namespace Xamarin.Forms.Core.UnitTests
[Test]
public void InvalidCtor()
{
Assert.Throws<ArgumentNullException>(() => new TypedBinding<MockViewModel, string>(null, (mvm, s) => mvm.Text = s, null), "Allowed null getter");
Assert.Throws<ArgumentNullException>(() => new TypedBinding<MockViewModel, string>((Func<MockViewModel, string>)null, (mvm, s) => mvm.Text = s, null), "Allowed null getter");
}
[Test, NUnit.Framework.Category("[Binding] Set Value")]

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms
@ -104,5 +103,15 @@ namespace Xamarin.Forms
}
bool IFlowDirectionController.ApplyEffectiveFlowDirectionToChildContainer => true;
double IFlowDirectionController.Width => (Parent as VisualElement)?.Width ?? 0;
internal virtual void ApplyQueryAttributes(IDictionary<string, string> query)
{
}
}
public interface IQueryAttributable
{
void ApplyQueryAttributes(IDictionary<string, string> query);
}
}

Просмотреть файл

@ -5,9 +5,8 @@ namespace Xamarin.Forms
[AttributeUsage(AttributeTargets.Class)]
public class QueryPropertyAttribute : Attribute
{
public string Name { get; private set; }
public string QueryId { get; private set; }
public string Name { get; }
public string QueryId { get; }
public QueryPropertyAttribute(string name, string queryId)
{

Просмотреть файл

@ -402,89 +402,35 @@ namespace Xamarin.Forms
prefix = route + ".";
}
var type = element.GetType();
var typeInfo = type.GetTypeInfo();
#if NETSTANDARD1_0
var effectAttributes = typeInfo.GetCustomAttributes(typeof(QueryPropertyAttribute), true).ToArray();
#else
var effectAttributes = typeInfo.GetCustomAttributes(typeof(QueryPropertyAttribute), true);
#endif
if (effectAttributes.Length == 0)
return;
foreach (var a in effectAttributes)
{
if (a is QueryPropertyAttribute attrib)
{
if (query.TryGetValue(prefix + attrib.QueryId, out var value))
{
PropertyInfo prop = type.GetRuntimeProperty(attrib.Name);
if (prop != null && prop.CanWrite && prop.SetMethod.IsPublic)
{
prop.SetValue(element, value);
}
}
}
}
}
static string GenerateQueryString(Dictionary<string, string> queryData)
{
if (queryData.Count == 0)
return string.Empty;
string result = "?";
bool addAnd = false;
foreach (var kvp in queryData)
{
if (addAnd)
result += "&";
result += kvp.Key + "=" + kvp.Value;
addAnd = true;
//if the lastItem is implicitly wrapped, get the actual ShellContent
if (isLastItem) {
if (element is ShellItem shellitem && shellitem.Items.FirstOrDefault() is ShellSection section)
element = section;
if (element is ShellSection shellsection && shellsection.Items.FirstOrDefault() is ShellContent content)
element = content;
if (element is ShellContent shellcontent && shellcontent.Content is Element e)
element = e;
}
return result;
}
static void GetQueryStringData(Element element, bool isLastItem, Dictionary<string, string> result)
{
string prefix = string.Empty;
if (!isLastItem)
if (!(element is BaseShellItem baseShellItem))
{
var route = Routing.GetRoute(element);
if (string.IsNullOrEmpty(route) || route.StartsWith(Routing.ImplicitPrefix, StringComparison.Ordinal))
baseShellItem = element?.Parent as BaseShellItem;
if(baseShellItem == null)
return;
prefix = route + ".";
}
var type = element.GetType();
var typeInfo = type.GetTypeInfo();
#if NETSTANDARD1_0
var effectAttributes = typeInfo.GetCustomAttributes(typeof(QueryPropertyAttribute), true).ToArray();
#else
var effectAttributes = type.GetCustomAttributes(typeof(QueryPropertyAttribute), true);
#endif
if (effectAttributes.Length == 0)
return;
for (int i = 0; i < effectAttributes.Length; i++)
{
if (effectAttributes[i] is QueryPropertyAttribute attrib)
{
PropertyInfo prop = type.GetRuntimeProperty(attrib.Name);
if (prop != null && prop.CanRead && prop.GetMethod.IsPublic)
{
var val = (string)prop.GetValue(element);
var key = isLastItem ? prefix + attrib.QueryId : attrib.QueryId;
result[key] = val;
}
}
//filter the query to only apply the keys with matching prefix
var filteredQuery = new Dictionary<string, string>(query.Count);
foreach (var q in query) {
if (!q.Key.StartsWith(prefix, StringComparison.Ordinal))
continue;
var key = q.Key.Substring(prefix.Length);
if (key.Contains("."))
continue;
filteredQuery.Add(key, q.Value);
}
baseShellItem.ApplyQueryAttributes(filteredQuery);
}
ShellNavigationState GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> sectionStack)
@ -494,8 +440,6 @@ namespace Xamarin.Forms
bool stackAtRoot = sectionStack == null || sectionStack.Count <= 1;
GetQueryStringData(this, false, queryData);
if (shellItem != null)
{
var shellItemRoute = shellItem.Route;
@ -505,8 +449,6 @@ namespace Xamarin.Forms
stateBuilder.Append("/");
}
GetQueryStringData(shellItem, shellSection == null, queryData);
if (shellSection != null)
{
var shellSectionRoute = shellSection.Route;
@ -516,8 +458,6 @@ namespace Xamarin.Forms
stateBuilder.Append("/");
}
GetQueryStringData(shellSection, shellContent == null && stackAtRoot, queryData);
if (shellContent != null)
{
var shellContentRoute = shellContent.Route;
@ -526,8 +466,6 @@ namespace Xamarin.Forms
stateBuilder.Append(shellContentRoute);
stateBuilder.Append("/");
}
GetQueryStringData(shellContent, stackAtRoot, queryData);
}
if (!stackAtRoot)
@ -536,7 +474,6 @@ namespace Xamarin.Forms
{
var page = sectionStack[i];
stateBuilder.Append(Routing.GetRoute(page));
GetQueryStringData(page, i == sectionStack.Count - 1, queryData);
if (i < sectionStack.Count - 1)
stateBuilder.Append("/");
}
@ -544,7 +481,6 @@ namespace Xamarin.Forms
}
}
stateBuilder.Append(GenerateQueryString(queryData));
return stateBuilder.ToString();
}
@ -800,15 +736,12 @@ namespace Xamarin.Forms
protected virtual void OnNavigated(ShellNavigatedEventArgs args)
{
if (_accumulateNavigatedEvents)
{
_accumulatedEvent = args;
}
else
{
if (args.Current.Location.AbsolutePath != _lastNavigating.Location.AbsolutePath)
{
throw new Exception();
}
else {
/* Removing this check for now as it doesn't properly cover all implicit scenarios
* if (args.Current.Location.AbsolutePath.TrimEnd('/') != _lastNavigating.Location.AbsolutePath.TrimEnd('/'))
throw new InvalidOperationException($"Navigation: Current location doesn't match navigation uri {args.Current.Location.AbsolutePath} != {_lastNavigating.Location.AbsolutePath}");
*/
Navigated?.Invoke(this, args);
//System.Diagnostics.Debug.WriteLine("Navigated: " + args.Current.Location);
}

Просмотреть файл

@ -1,6 +1,13 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
#if NETSTANDARD1_0
using System.Linq;
#endif
using System.Reflection;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms
@ -8,15 +15,29 @@ namespace Xamarin.Forms
[ContentProperty("Content")]
public class ShellContent : BaseShellItem, IShellContentController
{
#region PropertyKeys
static readonly BindablePropertyKey MenuItemsPropertyKey =
BindableProperty.CreateReadOnly(nameof(MenuItems), typeof(MenuItemCollection), typeof(ShellContent), null,
defaultValueCreator: bo => new MenuItemCollection());
#endregion PropertyKeys
public static readonly BindableProperty MenuItemsProperty = MenuItemsPropertyKey.BindableProperty;
#region IShellContentController
public static readonly BindableProperty ContentProperty =
BindableProperty.Create(nameof(Content), typeof(object), typeof(ShellContent), null, BindingMode.OneTime, propertyChanged: OnContentChanged);
public static readonly BindableProperty ContentTemplateProperty =
BindableProperty.Create(nameof(ContentTemplate), typeof(DataTemplate), typeof(ShellContent), null, BindingMode.OneTime);
public MenuItemCollection MenuItems => (MenuItemCollection)GetValue(MenuItemsProperty);
public object Content {
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public DataTemplate ContentTemplate {
get => (DataTemplate)GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
Page IShellContentController.Page => ContentCache;
@ -40,6 +61,12 @@ namespace Xamarin.Forms
if (result != null && result.Parent != this)
OnChildAdded(result);
if (_delayedQueryParams != null && result != null) {
ApplyQueryAttributes(result, _delayedQueryParams);
_delayedQueryParams = null;
}
return result;
}
@ -52,16 +79,6 @@ namespace Xamarin.Forms
}
}
#endregion IShellContentController
public static readonly BindableProperty ContentProperty =
BindableProperty.Create(nameof(Content), typeof(object), typeof(ShellContent), null, BindingMode.OneTime, propertyChanged: OnContentChanged);
public static readonly BindableProperty ContentTemplateProperty =
BindableProperty.Create(nameof(ContentTemplate), typeof(DataTemplate), typeof(ShellContent), null, BindingMode.OneTime);
public static readonly BindableProperty MenuItemsProperty = MenuItemsPropertyKey.BindableProperty;
Page _contentCache;
IList<Element> _logicalChildren = new List<Element>();
ReadOnlyCollection<Element> _logicalChildrenReadOnly;
@ -71,19 +88,7 @@ namespace Xamarin.Forms
((INotifyCollectionChanged)MenuItems).CollectionChanged += MenuItemsCollectionChanged;
}
public object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public DataTemplate ContentTemplate
{
get { return (DataTemplate)GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); }
}
public MenuItemCollection MenuItems => (MenuItemCollection)GetValue(MenuItemsProperty);
internal override ReadOnlyCollection<Element> LogicalChildrenInternal => _logicalChildrenReadOnly ?? (_logicalChildrenReadOnly = new ReadOnlyCollection<Element>(_logicalChildren));
Page ContentCache {
@ -142,15 +147,53 @@ namespace Xamarin.Forms
void MenuItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Element el in e.NewItems)
OnChildAdded(el);
}
if (e.OldItems != null)
{
foreach (Element el in e.OldItems)
OnChildRemoved(el);
}
IDictionary<string, string> _delayedQueryParams;
internal override void ApplyQueryAttributes(IDictionary<string, string> query)
{
base.ApplyQueryAttributes(query);
ApplyQueryAttributes(this, query);
if (Content == null) {
_delayedQueryParams = query;
return;
}
ApplyQueryAttributes(Content as Page, query);
}
static void ApplyQueryAttributes(object content, IDictionary<string, string> query)
{
if (content is IQueryAttributable attributable)
attributable.ApplyQueryAttributes(query);
if (content is BindableObject bindable && bindable.BindingContext != null && content != bindable.BindingContext)
ApplyQueryAttributes(bindable.BindingContext, query);
var type = content.GetType();
var typeInfo = type.GetTypeInfo();
#if NETSTANDARD1_0
var queryPropertyAttributes = typeInfo.GetCustomAttributes(typeof(QueryPropertyAttribute), true).ToArray();
#else
var queryPropertyAttributes = typeInfo.GetCustomAttributes(typeof(QueryPropertyAttribute), true);
#endif
if (queryPropertyAttributes.Length == 0)
return;
foreach (QueryPropertyAttribute attrib in queryPropertyAttributes) {
if (query.TryGetValue(attrib.QueryId, out var value)) {
PropertyInfo prop = type.GetRuntimeProperty(attrib.Name);
if (prop != null && prop.CanWrite && prop.SetMethod.IsPublic)
prop.SetValue(content, value);
}
}
}
}

Просмотреть файл

@ -58,11 +58,19 @@ namespace Xamarin.Forms.Internals
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class TypedBinding<TSource, TProperty> : TypedBindingBase
{
readonly Func<TSource, TProperty> _getter;
readonly Func<TSource, (TProperty value, bool success)> _getter;
readonly Action<TSource, TProperty> _setter;
readonly PropertyChangedProxy [] _handlers;
[Obsolete("deprecated one. kept for backcompat")]
public TypedBinding(Func<TSource, TProperty> getter, Action<TSource, TProperty> setter, Tuple<Func<TSource, object>, string> [] handlers)
: this (s=>(getter(s), true), setter, handlers)
{
if (getter == null)
throw new ArgumentNullException(nameof(getter));
}
public TypedBinding(Func<TSource, (TProperty value, bool success)> getter, Action<TSource, TProperty> setter, Tuple<Func<TSource, object>, string>[] handlers)
{
_getter = getter ?? throw new ArgumentNullException(nameof(getter));
_setter = setter;
@ -70,9 +78,9 @@ namespace Xamarin.Forms.Internals
if (handlers == null)
return;
_handlers = new PropertyChangedProxy [handlers.Length];
_handlers = new PropertyChangedProxy[handlers.Length];
for (var i = 0; i < handlers.Length; i++)
_handlers [i] = new PropertyChangedProxy(handlers [i].Item1, handlers [i].Item2, this);
_handlers[i] = new PropertyChangedProxy(handlers[i].Item1, handlers[i].Item2, this);
}
readonly WeakReference<object> _weakSource = new WeakReference<object>(null);
@ -196,7 +204,9 @@ namespace Xamarin.Forms.Internals
var value = FallbackValue ?? property.GetDefaultValue(target);
if (isTSource) {
try {
value = GetSourceValue(_getter((TSource)sourceObject), property.ReturnType);
(var retval, bool success) = _getter((TSource)sourceObject);
if (success) //if the getter failed, return the FallbackValue
value = GetSourceValue(retval, property.ReturnType);
} catch (Exception ex) when (ex is NullReferenceException || ex is KeyNotFoundException || ex is IndexOutOfRangeException) {
}
}

Просмотреть файл

@ -18,7 +18,7 @@
<Compile Include="Internals\Legacy\**" />
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<Import Project="..\Xamarin.Flex\Xamarin.Flex.projitems" Label="Shared" Condition="Exists('..\Xamarin.Flex\Xamarin.Flex.projitems')" />
<UsingTask TaskName="XFCorePostProcessor.Tasks.FixXFCoreAssembly" AssemblyFile="..\XFCorePostProcessor.Tasks\bin\Debug\net461\XFCorePostProcessor.Tasks.dll" />

Двоичные данные
Xamarin.Forms.Maps.9.9.9.nupkg

Двоичный файл не отображается.

Просмотреть файл

@ -235,6 +235,9 @@ namespace Xamarin.Forms.Platform.UWP
void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
if (args.NewValue == null)
return;
// We don't want to set the Cell until the ListView is realized, just in case the
// Cell has an ItemTemplate. Instead, we'll store the new data item, and it will be
// set on MeasureOverrideDelegate. However, if the parent is a TableView, we'll already

Просмотреть файл

@ -1,44 +0,0 @@
using System;
namespace Xamarin.Forms.Platform.iOS
{
public static class BarAppearanceTrackerUtils
{
public static Color UnblendColor(Color color)
{
// iOS is going to ignore any alpha mask on this color so we need
// to just assume its a solid color. We'll "blend" it with white.
var r = color.R;
var g = color.G;
var b = color.B;
var a = color.A;
r = (r * a) + (1 - a);
g = (g * a) + (1 - a);
b = (b * a) + (1 - a);
// Now iOS is going to blend with white at 86% opacity, so we need to "darken" the
// color we pass in order to compensate
r = UnblendBackgroundChannel(r, .863);
g = UnblendBackgroundChannel(g, .863);
b = UnblendBackgroundChannel(b, .863);
return new Color(r, g, b);
}
static double UnblendBackgroundChannel(double channel, double alpha)
{
// basically iOS is going to blend us with white to produce its final color,
// so we are going to apply the default blend in reverse to produce a true color.
// Unfortunately "unblending" only works for a subset of all possible values as you
// can get a negative number. This means that there is no source input value
// for which the requested color can be retreived.
// In this case we just clamp to 0
return Math.Max(0, (channel - (1 - alpha)) / alpha);
}
}
}

Просмотреть файл

@ -38,7 +38,7 @@ namespace Xamarin.Forms.Platform.iOS
}
if (!background.IsDefault)
navBar.BarTintColor = BarAppearanceTrackerUtils.UnblendColor(background).ToUIColor();
navBar.BarTintColor = background.ToUIColor();
if (!foreground.IsDefault)
navBar.TintColor = foreground.ToUIColor();
if (!titleColor.IsDefault)

Просмотреть файл

@ -34,7 +34,7 @@ namespace Xamarin.Forms.Platform.iOS
}
if (!background.IsDefault)
tabBar.BarTintColor = BarAppearanceTrackerUtils.UnblendColor(background).ToUIColor();
tabBar.BarTintColor = background.ToUIColor();
if (!foreground.IsDefault)
tabBar.TintColor = foreground.ToUIColor();
if (!unselectedColor.IsDefault)

Просмотреть файл

@ -30,7 +30,7 @@ namespace Xamarin.Forms.Platform.iOS
protected virtual void SetAppearance(ShellAppearance appearance)
{
SetValues(appearance.BackgroundColor.IsDefault ? _defaultBackgroundColor : BarAppearanceTrackerUtils.UnblendColor(appearance.BackgroundColor),
SetValues(appearance.BackgroundColor.IsDefault ? _defaultBackgroundColor : appearance.BackgroundColor,
appearance.ForegroundColor.IsDefault ? _defaultForegroundColor : appearance.ForegroundColor,
appearance.UnselectedColor.IsDefault ? _defaultUnselectedColor : appearance.UnselectedColor);
}

Просмотреть файл

@ -157,7 +157,6 @@
<Compile Include="Forms.cs" />
<Compile Include="PageExtensions.cs" />
<Compile Include="Renderers\WkWebViewRenderer.cs" />
<Compile Include="Renderers\BarAppearanceTrackerUtils.cs" />
<Compile Include="Renderers\ElementSelectedEventArgs.cs" />
<Compile Include="Renderers\IShellContext.cs" />
<Compile Include="Renderers\IShellFlyoutContentRenderer.cs" />

Просмотреть файл

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="using:Xamarin.Forms.Xaml.UnitTests"
x:Class="Xamarin.Forms.Xaml.UnitTests.Gh4102"
x:DataType="local:Gh4102VM">
<Label x:Name="label" Text="{Binding SomeNullValue.SomeProperty}"/>
</ContentPage>

Просмотреть файл

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
public class Gh4102VM
{
public Gh4102VM SomeNullValue { get; set; }
public string SomeProperty { get; set; } = "Foo";
}
public partial class Gh4102 : ContentPage
{
public Gh4102() => InitializeComponent();
public Gh4102(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}
[TestFixture]
class Tests
{
[SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
[TearDown] public void TearDown() => Device.PlatformServices = null;
[TestCase(true), TestCase(false)]
public void CompiledBindingsNullInPath(bool useCompiledXaml)
{
var layout = new Gh4102(useCompiledXaml) { BindingContext = new Gh4102VM() };
Assert.That(layout.label.Text, Is.EqualTo(null));
}
}
}
}

Просмотреть файл

@ -10,14 +10,10 @@ namespace Xamarin.Forms.Xaml.UnitTests
{
public string SomeNullableValue { get; set; } = "initial";
}
public partial class Gh4103 : ContentPage
{
private string _someNullableValue = "initial";
public Gh4103()
{
InitializeComponent();
}
public Gh4103() => InitializeComponent();
public Gh4103(bool useCompiledXaml)
{