зеркало из https://github.com/DeGsoft/maui-linux.git
Merge branch '4.2.0' into 4.3.0
This commit is contained in:
Коммит
0dfade22be
|
@ -113,8 +113,16 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
var br2 = Instruction.Create(OpCodes.Ldarg_0);
|
||||
var ret = Instruction.Create(OpCodes.Ret);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
var baseCtor = module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false));
|
||||
baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module));
|
||||
MethodReference baseCtor;
|
||||
if (typeDef.BaseType.Resolve().GetConstructors().FirstOrDefault(c => c.HasParameters && c.Parameters.Count == 1 && c.Parameters[0].Name == "useCompiledXaml") is MethodDefinition baseCtorDef) {
|
||||
baseCtor = module.ImportReference(baseCtorDef);
|
||||
baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module));
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
}
|
||||
else {
|
||||
baseCtor = module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false));
|
||||
baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module));
|
||||
}
|
||||
il.Emit(OpCodes.Callvirt, baseCtor);
|
||||
|
||||
il.Emit(OpCodes.Nop);
|
||||
|
|
|
@ -10,10 +10,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
{
|
||||
class SetNamescopesAndRegisterNamesVisitor : IXamlNodeVisitor
|
||||
{
|
||||
public SetNamescopesAndRegisterNamesVisitor(ILContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
public SetNamescopesAndRegisterNamesVisitor(ILContext context) => Context = context;
|
||||
|
||||
ILContext Context { get; }
|
||||
|
||||
|
@ -25,7 +22,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
public bool IsResourceDictionary(ElementNode node)
|
||||
{
|
||||
var parentVar = Context.Variables[(IElementNode)node];
|
||||
var parentVar = Context.Variables[node];
|
||||
return parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary"
|
||||
|| parentVar.VariableType.Resolve().BaseType?.FullName == "Xamarin.Forms.ResourceDictionary";
|
||||
}
|
||||
|
@ -64,7 +61,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
public void Visit(RootNode node, INode parentNode)
|
||||
{
|
||||
var namescopeVarDef = CreateNamescope();
|
||||
var namescopeVarDef = GetOrCreateNameScope(node);
|
||||
IList<string> namesInNamescope = new List<string>();
|
||||
if (Context.Variables[node].VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject"))))
|
||||
SetNameScope(node, namescopeVarDef);
|
||||
|
@ -77,33 +74,38 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
}
|
||||
|
||||
static bool IsDataTemplate(INode node, INode parentNode)
|
||||
{
|
||||
var parentElement = parentNode as IElementNode;
|
||||
INode createContent;
|
||||
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
|
||||
createContent == node)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
=> parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) && createContent == node;
|
||||
|
||||
static bool IsStyle(INode node, INode parentNode)
|
||||
{
|
||||
var pnode = parentNode as ElementNode;
|
||||
return pnode != null && pnode.XmlType.Name == "Style";
|
||||
}
|
||||
static bool IsStyle(INode node, INode parentNode) => parentNode is ElementNode pnode && pnode.XmlType.Name == "Style";
|
||||
|
||||
static bool IsVisualStateGroupList(ElementNode node)
|
||||
{
|
||||
return node != null && node.XmlType.Name == "VisualStateGroup" && node.Parent is IListNode;
|
||||
}
|
||||
static bool IsVisualStateGroupList(ElementNode node) => node != null && node.XmlType.Name == "VisualStateGroup" && node.Parent is IListNode;
|
||||
|
||||
static bool IsXNameProperty(ValueNode node, INode parentNode)
|
||||
=> parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName.xName, out INode xNameNode) && xNameNode == node;
|
||||
|
||||
VariableDefinition GetOrCreateNameScope(ElementNode node)
|
||||
{
|
||||
var parentElement = parentNode as IElementNode;
|
||||
INode xNameNode;
|
||||
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
|
||||
return true;
|
||||
return false;
|
||||
var module = Context.Body.Method.Module;
|
||||
var vardef = new VariableDefinition(module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope")));
|
||||
Context.Body.Variables.Add(vardef);
|
||||
var stloc = Instruction.Create(OpCodes.Stloc, vardef);
|
||||
|
||||
if (Context.Variables[node].VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject")))) {
|
||||
var namescoperef = ("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject");
|
||||
Context.IL.Append(Context.Variables[node].LoadAs(module.GetTypeDefinition(namescoperef), module));
|
||||
Context.IL.Emit(OpCodes.Call, module.ImportMethodReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope"),
|
||||
methodName: "GetNameScope",
|
||||
parameterTypes: new[] { namescoperef },
|
||||
isStatic: true));
|
||||
Context.IL.Emit(OpCodes.Dup);
|
||||
Context.IL.Emit(OpCodes.Brtrue, stloc);
|
||||
|
||||
Context.IL.Emit(OpCodes.Pop);
|
||||
}
|
||||
Context.IL.Emit(OpCodes.Newobj, module.ImportCtorReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope"), parameterTypes: null));
|
||||
|
||||
Context.IL.Append(stloc);
|
||||
return vardef;
|
||||
}
|
||||
|
||||
VariableDefinition CreateNamescope()
|
||||
|
|
|
@ -1,21 +1,41 @@
|
|||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
using System;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 2818, "Right-to-Left MasterDetail in Xamarin.Forms Hamburger icon issue", PlatformAffected.Android)]
|
||||
public class Issue2818 : MasterDetailPage
|
||||
public class Issue2818 : TestMasterDetailPage
|
||||
{
|
||||
public Issue2818()
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
FlowDirection = FlowDirection.RightToLeft;
|
||||
|
||||
Master = new ContentPage
|
||||
{
|
||||
Title = "Master",
|
||||
BackgroundColor = Color.SkyBlue,
|
||||
IconImageSource = "menuIcon"
|
||||
IconImageSource = "menuIcon",
|
||||
Content = new StackLayout()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Button()
|
||||
{
|
||||
Text = "If you can see me the test has passed",
|
||||
AutomationId = "CloseMasterView",
|
||||
Command = new Command(() => IsPresented = false)
|
||||
}
|
||||
},
|
||||
AutomationId = "MasterLayout"
|
||||
},
|
||||
Padding = new Thickness(0, 42, 0, 0)
|
||||
};
|
||||
|
||||
Detail = new NavigationPage(new ContentPage
|
||||
|
@ -26,21 +46,92 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
Children = {
|
||||
new Label
|
||||
{
|
||||
Text = "The page must be with RightToLeft FlowDirection. Hamburger icon in main page must be going to right side."
|
||||
Text = "The page must be with RightToLeft FlowDirection. Hamburger icon in main page must be going to right side. There should be visible text inside the Master View"
|
||||
},
|
||||
new Button
|
||||
{
|
||||
Text = "Set RightToLeft",
|
||||
Command = new Command(() => FlowDirection = FlowDirection.RightToLeft)
|
||||
Command = new Command(() => FlowDirection = FlowDirection.RightToLeft),
|
||||
AutomationId = "ShowRightToLeft"
|
||||
},
|
||||
new Button
|
||||
{
|
||||
Text = "Set LeftToRight",
|
||||
Command = new Command(() => FlowDirection = FlowDirection.LeftToRight)
|
||||
Command = new Command(() => FlowDirection = FlowDirection.LeftToRight),
|
||||
AutomationId = "ShowLeftToRight"
|
||||
},
|
||||
new Button
|
||||
{
|
||||
Text = "Open Master View",
|
||||
Command = new Command(() => IsPresented = true),
|
||||
AutomationId = "OpenMasterView"
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Device.Idiom.ToString(),
|
||||
AutomationId = "Idiom"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#if UITEST
|
||||
[Test]
|
||||
public void MasterViewMovesAndContentIsVisible()
|
||||
{
|
||||
var idiom = RunningApp.WaitForElement("Idiom");
|
||||
|
||||
// This behavior is currently broken on a phone device Issue 7270
|
||||
if (idiom[0].ReadText() != "Tablet")
|
||||
return;
|
||||
|
||||
RunningApp.Tap("OpenMasterView");
|
||||
RunningApp.Tap("CloseMasterView");
|
||||
RunningApp.SetOrientationLandscape();
|
||||
RunningApp.Tap("OpenMasterView");
|
||||
var positionStart = RunningApp.WaitForElement("CloseMasterView");
|
||||
RunningApp.Tap("ShowLeftToRight");
|
||||
|
||||
var results = RunningApp.QueryUntilPresent(() =>
|
||||
{
|
||||
var secondPosition = RunningApp.Query("CloseMasterView");
|
||||
|
||||
if (secondPosition.Length == 0)
|
||||
return null;
|
||||
|
||||
if (secondPosition[0].Rect.X < positionStart[0].Rect.X)
|
||||
return secondPosition;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
Assert.IsNotNull(results, "Master View Did not change flow direction correctly");
|
||||
Assert.AreEqual(1, results.Length, "Master View Did not change flow direction correctly");
|
||||
|
||||
}
|
||||
|
||||
#if __IOS__
|
||||
[Test]
|
||||
public void MasterViewSizeDoesntChangeAfterBackground()
|
||||
{
|
||||
var idiom = RunningApp.WaitForElement("Idiom");
|
||||
// This behavior is currently broken on a phone device Issue 7270
|
||||
if (idiom[0].ReadText() != "Tablet")
|
||||
return;
|
||||
|
||||
RunningApp.SetOrientationLandscape();
|
||||
RunningApp.Tap("CloseMasterView");
|
||||
RunningApp.Tap("ShowLeftToRight");
|
||||
var windowSize = RunningApp.WaitForElement("MasterLayout")[0];
|
||||
RunningApp.SendAppToBackground(TimeSpan.FromSeconds(5));
|
||||
var newWindowSize = RunningApp.WaitForElement("MasterLayout")[0];
|
||||
Assert.AreEqual(newWindowSize.Rect.Width, windowSize.Rect.Width);
|
||||
Assert.AreEqual(newWindowSize.Rect.Height, windowSize.Rect.Height);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ using System.IO;
|
|||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Queries;
|
||||
|
||||
#if __IOS__
|
||||
using Xamarin.UITest.iOS;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -445,6 +449,16 @@ namespace Xamarin.Forms.Controls
|
|||
{
|
||||
get { return _app.TestServer; }
|
||||
}
|
||||
|
||||
#if __IOS__
|
||||
public void SendAppToBackground(TimeSpan timeSpan)
|
||||
{
|
||||
if (_app is iOSApp app)
|
||||
{
|
||||
app.SendAppToBackground(timeSpan);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -21,6 +21,7 @@
|
|||
GalleryBuilder.NavButton("Header/Footer (Template)", () => new HeaderFooterTemplate(), Navigation),
|
||||
GalleryBuilder.NavButton("Header/Footer (Grid)", () => new HeaderFooterGrid(), Navigation),
|
||||
GalleryBuilder.NavButton("Footer Only (String)", () => new FooterOnlyString(), Navigation),
|
||||
GalleryBuilder.NavButton("Header/Footer (Grid Horizontal)", () => new HeaderFooterGridHorizontal(), Navigation),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?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:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.HeaderFooterGalleries.HeaderFooterGridHorizontal">
|
||||
<ContentPage.Content>
|
||||
<CollectionView x:Name="CollectionView" >
|
||||
<CollectionView.ItemsLayout>
|
||||
<GridItemsLayout Span="3" Orientation="Horizontal" HorizontalItemSpacing="4" VerticalItemSpacing="2"></GridItemsLayout>
|
||||
</CollectionView.ItemsLayout>
|
||||
|
||||
<CollectionView.Header>
|
||||
|
||||
<StackLayout>
|
||||
<Image Source="oasis.jpg" Aspect="AspectFill" HeightRequest="60"></Image>
|
||||
<Label Text="This Is A Header" TextColor="AntiqueWhite" HorizontalTextAlignment="Center"
|
||||
FontAttributes="Bold" FontSize="36" />
|
||||
<StackLayout Orientation="Horizontal">
|
||||
<Button Text="Add Content" Clicked="Handle_Clicked"></Button>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
|
||||
</CollectionView.Header>
|
||||
|
||||
<CollectionView.Footer>
|
||||
|
||||
<StackLayout>
|
||||
<Image Source="cover1.jpg" Aspect="AspectFill" HeightRequest="80"></Image>
|
||||
<Label Text="This Is A Footer" TextColor="AntiqueWhite" HorizontalTextAlignment="Center" Rotation="10"
|
||||
FontAttributes="Bold" FontSize="20" />
|
||||
<StackLayout Orientation="Horizontal">
|
||||
<Button Text="Add Content" Clicked="Handle_Clicked"></Button>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
|
||||
</CollectionView.Footer>
|
||||
|
||||
</CollectionView>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.HeaderFooterGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class HeaderFooterGridHorizontal : ContentPage
|
||||
{
|
||||
readonly DemoFilteredItemSource _demoFilteredItemSource = new DemoFilteredItemSource(10);
|
||||
|
||||
public HeaderFooterGridHorizontal()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
CollectionView.ItemTemplate = ExampleTemplates.PhotoTemplate();
|
||||
CollectionView.ItemsSource = _demoFilteredItemSource.Items;
|
||||
}
|
||||
|
||||
void Handle_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (sender is VisualElement ve && ve.Parent is StackLayout sl)
|
||||
sl.Children.Add(new Label() { Text = "Grow" });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,6 +80,9 @@
|
|||
<EmbeddedResource Update="GalleryPages\MapElementsGallery.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="GalleryPages\CollectionViewGalleries\HeaderFooterGalleries\HeaderFooterGridHorizontal.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="GalleryPages\MapGallery.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
|
|
|
@ -5,6 +5,9 @@ using Xamarin.UITest;
|
|||
using Xamarin.UITest.Queries;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
#if __IOS__
|
||||
using Xamarin.UITest.iOS;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.UITest
|
||||
{
|
||||
|
@ -28,6 +31,17 @@ namespace Xamarin.UITest
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
#if __IOS__
|
||||
public static void SendAppToBackground(this IApp app, TimeSpan timeSpan)
|
||||
{
|
||||
if(app is Xamarin.Forms.Controls.ScreenshotConditionalApp sca)
|
||||
{
|
||||
sca.SendAppToBackground(timeSpan);
|
||||
Thread.Sleep(timeSpan.Add(TimeSpan.FromSeconds(2)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,7 @@ namespace Xamarin.Forms.Internals
|
|||
_names[name] = scopedElement;
|
||||
}
|
||||
|
||||
public static INameScope GetNameScope(BindableObject bindable)
|
||||
{
|
||||
return (INameScope)bindable.GetValue(NameScopeProperty);
|
||||
}
|
||||
public static INameScope GetNameScope(BindableObject bindable) => (INameScope)bindable.GetValue(NameScopeProperty);
|
||||
|
||||
public static void SetNameScope(BindableObject bindable, INameScope value)
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
// If power saver is active, then animations will not run
|
||||
_energySaveModeDisabled = !powerSaveOn;
|
||||
|
||||
|
||||
// Notify the ticker that this value has changed, so it can manage animations in progress
|
||||
OnSystemEnabledChanged();
|
||||
}
|
||||
|
@ -56,8 +56,20 @@ namespace Xamarin.Forms.Platform.Android
|
|||
return false;
|
||||
}
|
||||
|
||||
var scale = global::Android.Provider.Settings.Global.GetFloat(resolver, global::Android.Provider.Settings.Global.AnimatorDurationScale, 0);
|
||||
return scale > 0;
|
||||
float animationScale;
|
||||
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr1)
|
||||
{
|
||||
animationScale = global::Android.Provider.Settings.Global.GetFloat(resolver, global::Android.Provider.Settings.Global.AnimatorDurationScale, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
#pragma warning disable 0618
|
||||
animationScale = global::Android.Provider.Settings.System.GetFloat(resolver, global::Android.Provider.Settings.System.AnimatorDurationScale, 1);
|
||||
#pragma warning restore 0618
|
||||
}
|
||||
|
||||
return animationScale > 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -95,4 +107,4 @@ namespace Xamarin.Forms.Platform.Android
|
|||
SendSignals();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
// If we're updating from a previous layout, we should keep any settings for the SelectableItemsViewController around
|
||||
var selectableItemsViewController = Delegator?.SelectableItemsViewController;
|
||||
Delegator = new UICollectionViewDelegator(ItemsViewLayout, this);
|
||||
|
||||
|
||||
CollectionView.Delegate = Delegator;
|
||||
|
||||
if (CollectionView.CollectionViewLayout != ItemsViewLayout)
|
||||
|
@ -54,9 +54,9 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
// Make sure the new layout is sized properly
|
||||
ItemsViewLayout.ConstrainTo(CollectionView.Bounds.Size);
|
||||
|
||||
|
||||
CollectionView.SetCollectionViewLayout(ItemsViewLayout, false);
|
||||
|
||||
|
||||
// Reload the data so the currently visible cells get laid out according to the new layout
|
||||
CollectionView.ReloadData();
|
||||
}
|
||||
|
@ -154,8 +154,19 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
// This update is only relevant if you have a footer view because it's used to place the footer view
|
||||
// based on the ContentSize so we just update the positions if the ContentSize has changed
|
||||
if(_footerUIView != null && _footerUIView.Frame.Y != ItemsViewLayout.CollectionViewContentSize.Height)
|
||||
UpdateHeaderFooterPosition();
|
||||
if (_footerUIView != null)
|
||||
{
|
||||
if (IsHorizontal)
|
||||
{
|
||||
if (_footerUIView.Frame.X != ItemsViewLayout.CollectionViewContentSize.Width)
|
||||
UpdateHeaderFooterPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_footerUIView.Frame.Y != ItemsViewLayout.CollectionViewContentSize.Height)
|
||||
UpdateHeaderFooterPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IItemsViewSource CreateItemsViewSource()
|
||||
|
@ -172,7 +183,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
public override nint NumberOfSections(UICollectionView collectionView)
|
||||
{
|
||||
CheckForEmptySource();
|
||||
CheckForEmptySource();
|
||||
return ItemsSource.GroupCount;
|
||||
}
|
||||
|
||||
|
@ -317,26 +328,53 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
CollectionView.RegisterClassForCell(typeof(VerticalTemplatedCell), VerticalTemplatedCell.ReuseId);
|
||||
}
|
||||
|
||||
bool IsHorizontal => (ItemsView?.ItemsLayout as ItemsLayout)?.Orientation == ItemsLayoutOrientation.Horizontal;
|
||||
|
||||
void UpdateHeaderFooterPosition()
|
||||
{
|
||||
var currentInset = CollectionView.ContentInset;
|
||||
|
||||
nfloat headerHeight = _headerUIView?.Frame.Height ?? 0f;
|
||||
nfloat footerHeight = _footerUIView?.Frame.Height ?? 0f;
|
||||
|
||||
if(_headerUIView != null && _headerUIView.Frame.Y != headerHeight)
|
||||
_headerUIView.Frame = new CoreGraphics.CGRect(0, -headerHeight, CollectionView.Frame.Width, headerHeight);
|
||||
|
||||
if (_footerUIView != null && (_footerUIView.Frame.Y != ItemsViewLayout.CollectionViewContentSize.Height))
|
||||
_footerUIView.Frame = new CoreGraphics.CGRect(0, ItemsViewLayout.CollectionViewContentSize.Height, CollectionView.Frame.Width, footerHeight);
|
||||
|
||||
if (CollectionView.ContentInset.Top != headerHeight || CollectionView.ContentInset.Bottom != footerHeight)
|
||||
if (IsHorizontal)
|
||||
{
|
||||
CollectionView.ContentInset = new UIEdgeInsets(headerHeight, 0, footerHeight, 0);
|
||||
var currentInset = CollectionView.ContentInset;
|
||||
|
||||
// if the header grows it will scroll off the screen because if you change the content inset iOS adjusts the content offset so the list doesn't move
|
||||
// this changes the offset of the list by how much ever the header size has changed
|
||||
CollectionView.ContentOffset = new CoreGraphics.CGPoint(CollectionView.ContentOffset.X, CollectionView.ContentOffset.Y + (currentInset.Top - CollectionView.ContentInset.Top));
|
||||
nfloat headerWidth = _headerUIView?.Frame.Width ?? 0f;
|
||||
nfloat footerWidth = _footerUIView?.Frame.Width ?? 0f;
|
||||
|
||||
if (_headerUIView != null && _headerUIView.Frame.X != headerWidth)
|
||||
_headerUIView.Frame = new CoreGraphics.CGRect(-headerWidth, 0, headerWidth, CollectionView.Frame.Height);
|
||||
|
||||
if (_footerUIView != null && (_footerUIView.Frame.X != ItemsViewLayout.CollectionViewContentSize.Width))
|
||||
_footerUIView.Frame = new CoreGraphics.CGRect(ItemsViewLayout.CollectionViewContentSize.Width, 0, footerWidth, CollectionView.Frame.Height);
|
||||
|
||||
if (CollectionView.ContentInset.Left != headerWidth || CollectionView.ContentInset.Right != footerWidth)
|
||||
{
|
||||
CollectionView.ContentInset = new UIEdgeInsets(0, headerWidth, 0, footerWidth);
|
||||
|
||||
// if the header grows it will scroll off the screen because if you change the content inset iOS adjusts the content offset so the list doesn't move
|
||||
// this changes the offset of the list by however much the header size has changed
|
||||
CollectionView.ContentOffset = new CoreGraphics.CGPoint(CollectionView.ContentOffset.X + (currentInset.Left - CollectionView.ContentInset.Left), CollectionView.ContentOffset.Y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentInset = CollectionView.ContentInset;
|
||||
|
||||
nfloat headerHeight = _headerUIView?.Frame.Height ?? 0f;
|
||||
nfloat footerHeight = _footerUIView?.Frame.Height ?? 0f;
|
||||
|
||||
if (_headerUIView != null && _headerUIView.Frame.Y != headerHeight)
|
||||
_headerUIView.Frame = new CoreGraphics.CGRect(0, -headerHeight, CollectionView.Frame.Width, headerHeight);
|
||||
|
||||
if (_footerUIView != null && (_footerUIView.Frame.Y != ItemsViewLayout.CollectionViewContentSize.Height))
|
||||
_footerUIView.Frame = new CoreGraphics.CGRect(0, ItemsViewLayout.CollectionViewContentSize.Height, CollectionView.Frame.Width, footerHeight);
|
||||
|
||||
if (CollectionView.ContentInset.Top != headerHeight || CollectionView.ContentInset.Bottom != footerHeight)
|
||||
{
|
||||
CollectionView.ContentInset = new UIEdgeInsets(headerHeight, 0, footerHeight, 0);
|
||||
|
||||
// if the header grows it will scroll off the screen because if you change the content inset iOS adjusts the content offset so the list doesn't move
|
||||
// this changes the offset of the list by however much the header size has changed
|
||||
CollectionView.ContentOffset = new CoreGraphics.CGPoint(CollectionView.ContentOffset.X, CollectionView.ContentOffset.Y + (currentInset.Top - CollectionView.ContentInset.Top));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,9 +417,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
if (formsElement != null)
|
||||
{
|
||||
var request = formsElement.Measure(CollectionView.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
|
||||
Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(formsElement, new Rectangle(0, -request.Request.Height, CollectionView.Frame.Width, request.Request.Height));
|
||||
|
||||
RemeasureLayout(formsElement);
|
||||
formsElement.MeasureInvalidated += OnFormsElementMeasureInvalidated;
|
||||
}
|
||||
else if (uiView != null)
|
||||
|
@ -390,13 +426,25 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
}
|
||||
}
|
||||
|
||||
void OnFormsElementMeasureInvalidated(object sender, EventArgs e)
|
||||
void RemeasureLayout(VisualElement formsElement)
|
||||
{
|
||||
if(sender is VisualElement formsElement)
|
||||
if (IsHorizontal)
|
||||
{
|
||||
var request = formsElement.Measure(double.PositiveInfinity, CollectionView.Frame.Height, MeasureFlags.IncludeMargins);
|
||||
Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(formsElement, new Rectangle(-request.Request.Width, 0, request.Request.Width, CollectionView.Frame.Height));
|
||||
}
|
||||
else
|
||||
{
|
||||
var request = formsElement.Measure(CollectionView.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
|
||||
Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(formsElement, new Rectangle(0, -request.Request.Height, CollectionView.Frame.Width, request.Request.Height));
|
||||
}
|
||||
}
|
||||
|
||||
void OnFormsElementMeasureInvalidated(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is VisualElement formsElement)
|
||||
{
|
||||
RemeasureLayout(formsElement);
|
||||
UpdateHeaderFooterPosition();
|
||||
}
|
||||
}
|
||||
|
@ -488,4 +536,4 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
return (new UILabel { Text = $"{view}" }, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,25 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
}
|
||||
}
|
||||
|
||||
internal static void UpdateFlowDirection(this UIView view, IVisualElementController controller)
|
||||
internal static bool UpdateFlowDirection(this UIView view, IVisualElementController controller)
|
||||
{
|
||||
if (controller == null || view == null || !Forms.IsiOS9OrNewer)
|
||||
return;
|
||||
return false;
|
||||
|
||||
UISemanticContentAttribute updateValue = view.SemanticContentAttribute;
|
||||
|
||||
if (controller.EffectiveFlowDirection.IsRightToLeft())
|
||||
view.SemanticContentAttribute = UISemanticContentAttribute.ForceRightToLeft;
|
||||
updateValue = UISemanticContentAttribute.ForceRightToLeft;
|
||||
else if (controller.EffectiveFlowDirection.IsLeftToRight())
|
||||
view.SemanticContentAttribute = UISemanticContentAttribute.ForceLeftToRight;
|
||||
updateValue = UISemanticContentAttribute.ForceLeftToRight;
|
||||
|
||||
if(updateValue != view.SemanticContentAttribute)
|
||||
{
|
||||
view.SemanticContentAttribute = updateValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void UpdateTextAlignment(this UITextField control, IVisualElementController controller)
|
||||
|
|
|
@ -195,7 +195,11 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
if (layoutMaster)
|
||||
{
|
||||
var masterBounds = _masterController.View.Frame;
|
||||
_masterWidth = (nfloat)Math.Max(_masterWidth, masterBounds.Width);
|
||||
|
||||
if (Forms.IsiOS13OrNewer)
|
||||
_masterWidth = masterBounds.Width;
|
||||
else
|
||||
_masterWidth = (nfloat)Math.Max(_masterWidth, masterBounds.Width);
|
||||
|
||||
if (!masterBounds.IsEmpty)
|
||||
MasterDetailPage.MasterBounds = new Rectangle(0, 0, _masterWidth, masterBounds.Height);
|
||||
|
@ -385,7 +389,12 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
void UpdateFlowDirection()
|
||||
{
|
||||
NativeView.UpdateFlowDirection(Element);
|
||||
if(NativeView.UpdateFlowDirection(Element) && Forms.IsiOS13OrNewer && NativeView.Superview != null)
|
||||
{
|
||||
var view = NativeView.Superview;
|
||||
NativeView.RemoveFromSuperview();
|
||||
view.AddSubview(NativeView);
|
||||
}
|
||||
}
|
||||
|
||||
class InnerDelegate : UISplitViewControllerDelegate
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<local:Gh7097Base
|
||||
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.Gh7097"
|
||||
Title="Foo"
|
||||
x:Name="self">
|
||||
<CollectionView ItemsSource="{Binding Items}" x:Name="collectionview">
|
||||
<CollectionView.ItemsLayout>
|
||||
<ListItemsLayout ItemSpacing="4">
|
||||
<x:Arguments>
|
||||
<ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
|
||||
</x:Arguments>
|
||||
</ListItemsLayout>
|
||||
</CollectionView.ItemsLayout>
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="Green">
|
||||
<Button
|
||||
Text="BTN 1"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
CommandParameter="{Binding}"
|
||||
Command="{Binding BindingContext.Button1Command, Source={x:Reference self}}"/>
|
||||
<Button
|
||||
Text="BTN 2"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
CommandParameter="{Binding}"
|
||||
Command="{Binding BindingContext.Button2Command, Source={x:Reference self}}"/>
|
||||
</StackLayout>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
</local:Gh7097Base>
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Input;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public partial class Gh7097 : Gh7097Base
|
||||
{
|
||||
public Gh7097() => InitializeComponent();
|
||||
public Gh7097(bool useCompiledXaml) : base(useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class Tests
|
||||
{
|
||||
IReadOnlyList<string> _flags;
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
_flags = Device.Flags;
|
||||
Device.SetFlags(new List<string>(Device.Flags ?? new List<string>()) { "CollectionView_Experimental" }.AsReadOnly());
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
Device.SetFlags(_flags);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanXReferenceRoot([Values(false, true)]bool useCompiledXaml)
|
||||
{
|
||||
var layout = new Gh7097(useCompiledXaml) { BindingContext = new {
|
||||
Button1Command = new MockCommand(),
|
||||
Button2Command = new MockCommand(),
|
||||
} };
|
||||
var cv = layout.Content as CollectionView;
|
||||
var content = cv.ItemTemplate.CreateContent() as StackLayout;
|
||||
var btn1 = content.Children[0] as Button;
|
||||
Assert.That(btn1.Command, Is.TypeOf<MockCommand>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
//this was later reported as https://github.com/xamarin/Xamarin.Forms/issues/7286
|
||||
public void RegisteringXNameOnSubPages([Values(false, true)]bool useCompiledXaml)
|
||||
{
|
||||
var layout = new Gh7097(useCompiledXaml);
|
||||
var s = layout.FindByName("self");
|
||||
Assert.That(layout.self, Is.Not.Null);
|
||||
Assert.That(layout.collectionview, Is.Not.Null);
|
||||
}
|
||||
|
||||
class MockCommand : ICommand
|
||||
{
|
||||
#pragma warning disable 0067
|
||||
public event EventHandler CanExecuteChanged;
|
||||
#pragma warning restore 0067
|
||||
public bool CanExecute(object parameter) => true;
|
||||
public void Execute(object parameter) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?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.Gh7097Base">
|
||||
<ContentPage.ControlTemplate>
|
||||
<ControlTemplate>
|
||||
<StackLayout>
|
||||
<Label Text="BASE" FontSize="30" FontAttributes="Bold" HorizontalOptions="CenterAndExpand" TextColor="Red"/>
|
||||
<ContentPresenter HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
|
||||
</StackLayout>
|
||||
</ControlTemplate>
|
||||
</ContentPage.ControlTemplate>
|
||||
</ContentPage>
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public partial class Gh7097Base : ContentPage
|
||||
{
|
||||
public Gh7097Base() => InitializeComponent();
|
||||
public Gh7097Base(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,11 +36,7 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
|
||||
public List<XmlName> SkipProperties { get; set; }
|
||||
|
||||
public Forms.Internals.INameScope Namescope {
|
||||
get {
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
public NameScopeRef NameScopeRef => throw new NotImplementedException();
|
||||
|
||||
public XmlType XmlType {
|
||||
get;
|
||||
|
|
|
@ -157,8 +157,7 @@ namespace Xamarin.Forms.Xaml
|
|||
Exception xpe = null;
|
||||
var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null;
|
||||
|
||||
object _;
|
||||
var collection = GetPropertyValue(source, parentList.XmlName, Context, parentList, out _) as IEnumerable;
|
||||
var collection = GetPropertyValue(source, parentList.XmlName, Context, parentList, out _, out _) as IEnumerable;
|
||||
if (collection == null)
|
||||
xpe = new XamlParseException($"Property {parentList.XmlName.LocalName} is null or is not IEnumerable", node);
|
||||
|
||||
|
@ -363,11 +362,10 @@ namespace Xamarin.Forms.Xaml
|
|||
throw xpe;
|
||||
}
|
||||
|
||||
public static object GetPropertyValue(object xamlElement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo, out object targetProperty)
|
||||
public static object GetPropertyValue(object xamlElement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo, out Exception xpe, out object targetProperty)
|
||||
{
|
||||
var localName = propertyName.LocalName;
|
||||
Exception xpe = null;
|
||||
object value;
|
||||
xpe = null;
|
||||
targetProperty = null;
|
||||
|
||||
//If it's an attached BP, update elementType and propertyName
|
||||
|
@ -376,7 +374,7 @@ namespace Xamarin.Forms.Xaml
|
|||
var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
|
||||
|
||||
//If it's a BindableProberty, GetValue
|
||||
if (xpe == null && TryGetValue(xamlElement, property, attached, out value, lineInfo, out xpe, out targetProperty))
|
||||
if (xpe == null && TryGetValue(xamlElement, property, attached, out var value, lineInfo, out xpe, out targetProperty))
|
||||
return value;
|
||||
|
||||
//If it's a normal property, get it
|
||||
|
@ -384,10 +382,6 @@ namespace Xamarin.Forms.Xaml
|
|||
return value;
|
||||
|
||||
xpe = xpe ?? new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
|
||||
if (context.ExceptionHandler != null)
|
||||
context.ExceptionHandler(xpe);
|
||||
else
|
||||
throw xpe;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -638,11 +632,7 @@ namespace Xamarin.Forms.Xaml
|
|||
static bool TryAddToProperty(object element, XmlName propertyName, object value, string xKey, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
|
||||
object targetProperty;
|
||||
var collection = GetPropertyValue(element, propertyName, context, lineInfo, out targetProperty) as IEnumerable;
|
||||
|
||||
if (collection == null)
|
||||
if (!(GetPropertyValue(element, propertyName, context, lineInfo, out _, out var targetProperty) is IEnumerable collection))
|
||||
return false;
|
||||
|
||||
if (exception == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, lineInfo, out exception))
|
||||
|
|
|
@ -141,8 +141,8 @@ namespace Xamarin.Forms.Xaml
|
|||
Values[node] = value;
|
||||
}
|
||||
|
||||
if (value is BindableObject bindableValue && node.Namescope != (parentNode as IElementNode)?.Namescope)
|
||||
NameScope.SetNameScope(bindableValue, node.Namescope);
|
||||
if (value is BindableObject bindableValue && node.NameScopeRef != (parentNode as IElementNode)?.NameScopeRef)
|
||||
NameScope.SetNameScope(bindableValue, node.NameScopeRef.NameScope);
|
||||
|
||||
if (XamlLoader.ValueCreatedCallback != null) {
|
||||
var name = node.XmlType.Name;
|
||||
|
@ -157,16 +157,18 @@ namespace Xamarin.Forms.Xaml
|
|||
var rnode = (XamlLoader.RuntimeRootNode)node;
|
||||
Values[node] = rnode.Root;
|
||||
Context.Types[node] = rnode.Root.GetType();
|
||||
var bindableRoot = rnode.Root as BindableObject;
|
||||
if (bindableRoot != null)
|
||||
NameScope.SetNameScope(bindableRoot, node.Namescope);
|
||||
if (rnode.Root is BindableObject bindable) {
|
||||
if (NameScope.GetNameScope(bindable) is INameScope existingNs)
|
||||
node.NameScopeRef.NameScope = existingNs;
|
||||
else
|
||||
NameScope.SetNameScope(bindable, node.NameScopeRef?.NameScope);
|
||||
}
|
||||
}
|
||||
|
||||
public void Visit(ListNode node, INode parentNode)
|
||||
{
|
||||
//this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
|
||||
XmlName name;
|
||||
if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
|
||||
if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out XmlName name))
|
||||
node.XmlName = name;
|
||||
}
|
||||
|
||||
|
@ -181,14 +183,12 @@ namespace Xamarin.Forms.Xaml
|
|||
ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
|
||||
if (ctorInfo == null)
|
||||
return true;
|
||||
foreach (var parameter in ctorInfo.GetParameters())
|
||||
{
|
||||
foreach (var parameter in ctorInfo.GetParameters()) {
|
||||
var propname =
|
||||
parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
|
||||
.ConstructorArguments.First()
|
||||
.Value as string;
|
||||
if (!node.Properties.ContainsKey(new XmlName("", propname)))
|
||||
{
|
||||
if (!node.Properties.ContainsKey(new XmlName("", propname))) {
|
||||
missingArgName = propname;
|
||||
return false;
|
||||
}
|
||||
|
@ -219,8 +219,7 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
object[] arguments = CreateArgumentsArray(node);
|
||||
|
||||
if (!node.Properties.ContainsKey(XmlName.xFactoryMethod))
|
||||
{
|
||||
if (!node.Properties.ContainsKey(XmlName.xFactoryMethod)) {
|
||||
//non-default ctor
|
||||
try {
|
||||
return Activator.CreateInstance(nodeType, arguments);
|
||||
|
@ -232,7 +231,9 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
|
||||
Type[] types = arguments == null ? new Type[0] : arguments.Select(a => a.GetType()).ToArray();
|
||||
Func<MethodInfo, bool> isMatch = m => {
|
||||
|
||||
bool isMatch(MethodInfo m)
|
||||
{
|
||||
if (m.Name != factoryMethod)
|
||||
return false;
|
||||
var p = m.GetParameters();
|
||||
|
@ -241,17 +242,18 @@ namespace Xamarin.Forms.Xaml
|
|||
if (!m.IsStatic)
|
||||
return false;
|
||||
for (var i = 0; i < p.Length; i++) {
|
||||
if ((p [i].ParameterType.IsAssignableFrom(types [i])))
|
||||
if ((p[i].ParameterType.IsAssignableFrom(types[i])))
|
||||
continue;
|
||||
var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
|
||||
var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
|
||||
?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
|
||||
|
||||
if (op_impl == null)
|
||||
return false;
|
||||
arguments [i] = op_impl.Invoke(null, new [] { arguments [i]});
|
||||
arguments[i] = op_impl.Invoke(null, new[] { arguments[i] });
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
|
||||
if (mi == null)
|
||||
|
@ -268,17 +270,13 @@ namespace Xamarin.Forms.Xaml
|
|||
if (!enode.Properties.ContainsKey(XmlName.xArguments))
|
||||
return null;
|
||||
var node = enode.Properties[XmlName.xArguments];
|
||||
var elementNode = node as ElementNode;
|
||||
if (elementNode != null)
|
||||
{
|
||||
if (node is ElementNode elementNode) {
|
||||
var array = new object[1];
|
||||
array[0] = Values[elementNode];
|
||||
return array;
|
||||
}
|
||||
|
||||
var listnode = node as ListNode;
|
||||
if (listnode != null)
|
||||
{
|
||||
if (node is ListNode listnode) {
|
||||
var array = new object[listnode.CollectionItems.Count];
|
||||
for (var i = 0; i < listnode.CollectionItems.Count; i++)
|
||||
array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
|
||||
|
@ -291,21 +289,15 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
var n = ctorInfo.GetParameters().Length;
|
||||
var array = new object[n];
|
||||
for (var i = 0; i < n; i++)
|
||||
{
|
||||
for (var i = 0; i < n; i++) {
|
||||
var parameter = ctorInfo.GetParameters()[i];
|
||||
var propname =
|
||||
parameter.CustomAttributes.First(attr => attr.AttributeType == typeof (ParameterAttribute))
|
||||
.ConstructorArguments.First()
|
||||
.Value as string;
|
||||
var name = new XmlName("", propname);
|
||||
INode node;
|
||||
if (!enode.Properties.TryGetValue(name, out node))
|
||||
{
|
||||
throw new XamlParseException(
|
||||
String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName),
|
||||
enode as IXmlLineInfo);
|
||||
}
|
||||
if (!enode.Properties.TryGetValue(name, out INode node))
|
||||
throw new XamlParseException($"The Property {propname} is required to create a {ctorInfo.DeclaringType.FullName} object.", enode as IXmlLineInfo);
|
||||
if (!enode.SkipProperties.Contains(name))
|
||||
enode.SkipProperties.Add(name);
|
||||
var value = Context.Values[node];
|
||||
|
@ -319,14 +311,11 @@ namespace Xamarin.Forms.Xaml
|
|||
return array;
|
||||
}
|
||||
|
||||
static bool IsXaml2009LanguagePrimitive(IElementNode node)
|
||||
{
|
||||
return node.NamespaceURI == XamlParser.X2009Uri;
|
||||
}
|
||||
static bool IsXaml2009LanguagePrimitive(IElementNode node) => node.NamespaceURI == XamlParser.X2009Uri;
|
||||
|
||||
static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
|
||||
{
|
||||
object value = null;
|
||||
object value;
|
||||
if (nodeType == typeof (string))
|
||||
value = String.Empty;
|
||||
else if (nodeType == typeof (Uri))
|
||||
|
@ -334,95 +323,43 @@ namespace Xamarin.Forms.Xaml
|
|||
else
|
||||
value = Activator.CreateInstance(nodeType);
|
||||
|
||||
if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
|
||||
((ValueNode)node.CollectionItems[0]).Value is string)
|
||||
{
|
||||
var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
|
||||
|
||||
if (nodeType == typeof(SByte)) {
|
||||
sbyte retval;
|
||||
if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(Int16)) {
|
||||
short retval;
|
||||
if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(Int32)) {
|
||||
int retval;
|
||||
if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(Int64)) {
|
||||
long retval;
|
||||
if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(Byte)) {
|
||||
byte retval;
|
||||
if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(UInt16)) {
|
||||
ushort retval;
|
||||
if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(UInt32)) {
|
||||
uint retval;
|
||||
if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(UInt64)) {
|
||||
ulong retval;
|
||||
if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(Single)) {
|
||||
float retval;
|
||||
if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(Double)) {
|
||||
double retval;
|
||||
if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof (Boolean))
|
||||
{
|
||||
bool outbool;
|
||||
if (bool.TryParse(valuestring, out outbool))
|
||||
return outbool;
|
||||
}
|
||||
if (nodeType == typeof(TimeSpan)) {
|
||||
TimeSpan retval;
|
||||
if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof (char))
|
||||
{
|
||||
char retval;
|
||||
if (char.TryParse(valuestring, out retval))
|
||||
return retval;
|
||||
}
|
||||
if ( node.CollectionItems.Count == 1
|
||||
&& node.CollectionItems[0] is ValueNode
|
||||
&& ((ValueNode)node.CollectionItems[0]).Value is string valuestring) {
|
||||
if (nodeType == typeof(SByte) && sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var sbyteval))
|
||||
return sbyteval;
|
||||
if (nodeType == typeof(Int16) && short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int16val))
|
||||
return int16val;
|
||||
if (nodeType == typeof(Int32) && int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int32val))
|
||||
return int32val;
|
||||
if (nodeType == typeof(Int64) && long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int64val))
|
||||
return int64val;
|
||||
if (nodeType == typeof(Byte) && byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var byteval))
|
||||
return byteval;
|
||||
if (nodeType == typeof(UInt16) && ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint16val))
|
||||
return uint16val;
|
||||
if (nodeType == typeof(UInt32) && uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint32val))
|
||||
return uint32val;
|
||||
if (nodeType == typeof(UInt64) && ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint64val))
|
||||
return uint64val;
|
||||
if (nodeType == typeof(Single) && float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var singleval))
|
||||
return singleval;
|
||||
if (nodeType == typeof(Double) && double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var doubleval))
|
||||
return doubleval;
|
||||
if (nodeType == typeof(Boolean) && bool.TryParse(valuestring, out var boolval))
|
||||
return boolval;
|
||||
if (nodeType == typeof(TimeSpan) && TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out TimeSpan timespanval))
|
||||
return timespanval;
|
||||
if (nodeType == typeof(char) && char.TryParse(valuestring, out var charval))
|
||||
return charval;
|
||||
if (nodeType == typeof (string))
|
||||
return valuestring;
|
||||
if (nodeType == typeof (decimal))
|
||||
{
|
||||
decimal retval;
|
||||
if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
|
||||
return retval;
|
||||
}
|
||||
|
||||
else if (nodeType == typeof (Uri))
|
||||
{
|
||||
Uri retval;
|
||||
if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))
|
||||
return retval;
|
||||
}
|
||||
if (nodeType == typeof(decimal) && decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var decimalval))
|
||||
return decimalval;
|
||||
if (nodeType == typeof(Uri) && Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out Uri urival))
|
||||
return urival;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,12 +5,11 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
class NamescopingVisitor : IXamlNodeVisitor
|
||||
{
|
||||
readonly Dictionary<INode, INameScope> _scopes = new Dictionary<INode, INameScope>();
|
||||
readonly Dictionary<INode, NameScopeRef> _scopes = new Dictionary<INode, NameScopeRef>();
|
||||
|
||||
public NamescopingVisitor(HydrationContext context)
|
||||
=> Values = context.Values;
|
||||
|
||||
Dictionary<INode, object> Values { get; set; }
|
||||
{
|
||||
}
|
||||
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => false;
|
||||
|
@ -19,37 +18,29 @@ namespace Xamarin.Forms.Xaml
|
|||
public bool SkipChildren(INode node, INode parentNode) => false;
|
||||
public bool IsResourceDictionary(ElementNode node) => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
=> _scopes[node] = _scopes[parentNode];
|
||||
|
||||
public void Visit(MarkupNode node, INode parentNode)
|
||||
=> _scopes[node] = _scopes[parentNode];
|
||||
public void Visit(ValueNode node, INode parentNode) => _scopes[node] = _scopes[parentNode];
|
||||
public void Visit(MarkupNode node, INode parentNode) => _scopes[node] = _scopes[parentNode];
|
||||
|
||||
public void Visit(ElementNode node, INode parentNode)
|
||||
=> _scopes[node] = node.Namescope = (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node))
|
||||
? new NameScope()
|
||||
=> _scopes[node] = node.NameScopeRef = (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node))
|
||||
? new NameScopeRef { NameScope = new NameScope() }
|
||||
: _scopes[parentNode];
|
||||
|
||||
public void Visit(RootNode node, INode parentNode)
|
||||
=> _scopes[node] = node.Namescope = new NameScope();
|
||||
public void Visit(RootNode node, INode parentNode) => _scopes[node] = node.NameScopeRef = new NameScopeRef { NameScope = new NameScope() };
|
||||
|
||||
public void Visit(ListNode node, INode parentNode) =>
|
||||
_scopes[node] = _scopes[parentNode];
|
||||
|
||||
static bool IsDataTemplate(INode node, INode parentNode)
|
||||
{
|
||||
var parentElement = parentNode as IElementNode;
|
||||
if ( parentElement != null
|
||||
&& parentElement.Properties.TryGetValue(XmlName._CreateContent, out var createContent)
|
||||
&& createContent == node)
|
||||
if ( parentNode is IElementNode parentElement
|
||||
&& parentElement.Properties.TryGetValue(XmlName._CreateContent, out var createContent)
|
||||
&& createContent == node)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsStyle(INode node, INode parentNode)
|
||||
=> (parentNode as ElementNode)?.XmlType.Name == "Style";
|
||||
|
||||
static bool IsVisualStateGroupList(ElementNode node)
|
||||
=> node?.XmlType.Name == "VisualStateGroup" && node?.Parent is IListNode;
|
||||
static bool IsStyle(INode node, INode parentNode) => (parentNode as ElementNode)?.XmlType.Name == "Style";
|
||||
static bool IsVisualStateGroupList(ElementNode node) => node?.XmlType.Name == "VisualStateGroup" && node?.Parent is IListNode;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.Xaml.Internals;
|
||||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
|
@ -32,7 +27,7 @@ namespace Xamarin.Forms.Xaml
|
|||
return;
|
||||
|
||||
try {
|
||||
((IElementNode)parentNode).Namescope.RegisterName((string)node.Value, Values[parentNode]);
|
||||
((IElementNode)parentNode).NameScopeRef.NameScope.RegisterName((string)node.Value, Values[parentNode]);
|
||||
}
|
||||
catch (ArgumentException ae) {
|
||||
if (ae.ParamName != "name")
|
||||
|
@ -73,12 +68,6 @@ namespace Xamarin.Forms.Xaml
|
|||
}
|
||||
|
||||
static bool IsXNameProperty(ValueNode node, INode parentNode)
|
||||
{
|
||||
var parentElement = parentNode as IElementNode;
|
||||
INode xNameNode;
|
||||
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
=> parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName.xName, out INode xNameNode) && xNameNode == node;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
Dictionary<XmlName, INode> Properties { get; }
|
||||
List<XmlName> SkipProperties { get; }
|
||||
INameScope Namescope { get; }
|
||||
NameScopeRef NameScopeRef { get; }
|
||||
XmlType XmlType { get; }
|
||||
string NamespaceURI { get; }
|
||||
}
|
||||
|
@ -34,6 +34,11 @@ namespace Xamarin.Forms.Xaml
|
|||
List<INode> CollectionItems { get; }
|
||||
}
|
||||
|
||||
class NameScopeRef
|
||||
{
|
||||
public INameScope NameScope { get; set; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{NamespaceUri}:{Name}")]
|
||||
class XmlType
|
||||
{
|
||||
|
@ -112,6 +117,7 @@ namespace Xamarin.Forms.Xaml
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
[DebuggerDisplay("{XmlType.Name}")]
|
||||
class ElementNode : BaseNode, IValueNode, IElementNode
|
||||
{
|
||||
|
@ -131,7 +137,7 @@ namespace Xamarin.Forms.Xaml
|
|||
public List<INode> CollectionItems { get; }
|
||||
public XmlType XmlType { get; }
|
||||
public string NamespaceURI { get; }
|
||||
public INameScope Namescope { get; set; }
|
||||
public NameScopeRef NameScopeRef { get; set; }
|
||||
|
||||
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
||||
{
|
||||
|
@ -152,10 +158,8 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
bool IsDataTemplate(INode parentNode)
|
||||
{
|
||||
var parentElement = parentNode as IElementNode;
|
||||
INode createContent;
|
||||
if (parentElement != null &&
|
||||
parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
|
||||
if (parentNode is IElementNode parentElement &&
|
||||
parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) &&
|
||||
createContent == this)
|
||||
return true;
|
||||
return false;
|
||||
|
|
|
@ -74,16 +74,9 @@ namespace Xamarin.Forms.Xaml.Internals
|
|||
set { services[typeof (IValueConverterProvider)] = value; }
|
||||
}
|
||||
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
object service;
|
||||
return services.TryGetValue(serviceType, out service) ? service : null;
|
||||
}
|
||||
public object GetService(Type serviceType) => services.TryGetValue(serviceType, out var service) ? service : null;
|
||||
|
||||
public void Add(Type type, object service)
|
||||
{
|
||||
services.Add(type, service);
|
||||
}
|
||||
public void Add(Type type, object service) => services.Add(type, service);
|
||||
}
|
||||
|
||||
class XamlValueTargetProvider : IProvideParentValues, IProvideValueTarget
|
||||
|
@ -104,21 +97,16 @@ namespace Xamarin.Forms.Xaml.Internals
|
|||
|
||||
IEnumerable<object> IProvideParentValues.ParentObjects
|
||||
{
|
||||
get
|
||||
{
|
||||
get {
|
||||
if (Node == null || Context == null)
|
||||
yield break;
|
||||
var n = Node;
|
||||
object obj = null;
|
||||
var context = Context;
|
||||
while (n.Parent != null && context != null)
|
||||
{
|
||||
if (n.Parent is IElementNode)
|
||||
{
|
||||
if (context.Values.TryGetValue(n.Parent, out obj))
|
||||
while (n.Parent != null && context != null) {
|
||||
if (n.Parent is IElementNode) {
|
||||
if (context.Values.TryGetValue(n.Parent, out var obj))
|
||||
yield return obj;
|
||||
else
|
||||
{
|
||||
else {
|
||||
context = context.ParentContext;
|
||||
continue;
|
||||
}
|
||||
|
@ -159,14 +147,9 @@ namespace Xamarin.Forms.Xaml.Internals
|
|||
this.scope = scope;
|
||||
}
|
||||
|
||||
IEnumerable<object> IProvideParentValues.ParentObjects
|
||||
=> objectAndParents;
|
||||
|
||||
object IProvideValueTarget.TargetObject
|
||||
=> objectAndParents[0];
|
||||
|
||||
object IProvideValueTarget.TargetProperty
|
||||
=> targetProperty;
|
||||
IEnumerable<object> IProvideParentValues.ParentObjects => objectAndParents;
|
||||
object IProvideValueTarget.TargetObject => objectAndParents[0];
|
||||
object IProvideValueTarget.TargetProperty => targetProperty;
|
||||
|
||||
public object FindByName(string name)
|
||||
{
|
||||
|
@ -277,15 +260,14 @@ namespace Xamarin.Forms.Xaml.Internals
|
|||
class ReferenceProvider : IReferenceProvider
|
||||
{
|
||||
readonly INode _node;
|
||||
internal ReferenceProvider(INode node)
|
||||
=> _node = node;
|
||||
internal ReferenceProvider(INode node) => _node = node;
|
||||
|
||||
public object FindByName(string name)
|
||||
{
|
||||
var n = _node;
|
||||
object value = null;
|
||||
while (n != null) {
|
||||
if ((value = (n as IElementNode)?.Namescope?.FindByName(name)) != null)
|
||||
object value;
|
||||
if ((value = (n as IElementNode)?.NameScopeRef.NameScope?.FindByName(name)) != null)
|
||||
return value;
|
||||
n = n.Parent;
|
||||
}
|
||||
|
@ -304,27 +286,16 @@ namespace Xamarin.Forms.Xaml.Internals
|
|||
{
|
||||
readonly Dictionary<string, string> namespaces = new Dictionary<string, string>();
|
||||
|
||||
public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) => throw new NotImplementedException();
|
||||
|
||||
public string LookupNamespace(string prefix)
|
||||
{
|
||||
string result;
|
||||
if (namespaces.TryGetValue(prefix, out result))
|
||||
if (namespaces.TryGetValue(prefix, out var result))
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
public string LookupPrefix(string namespaceName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Add(string prefix, string ns)
|
||||
{
|
||||
namespaces.Add(prefix, ns);
|
||||
}
|
||||
public string LookupPrefix(string namespaceName) => throw new NotImplementedException();
|
||||
public void Add(string prefix, string ns) => namespaces.Add(prefix, ns);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче