Merge branch 'main' into main-handler

This commit is contained in:
Rui Marinho 2021-01-25 14:40:08 +00:00
Родитель b45e87f45e 4215a10488
Коммит 9dcdee2ace
29 изменённых файлов: 661 добавлений и 165 удалений

23
.github/workflows/backport-trigger.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
name: Backport Trigger
on:
issue_comment:
types: [created]
jobs:
launchBackportBuild:
runs-on: ubuntu-latest
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '@vs-mobiletools-engineering-service2 backport')
steps:
- uses: xamarin/backport-bot-action@v1.0
with:
pull_request_url: ${{ github.event.issue.pull_request.url }}
comment_body: ${{ github.event.comment.body }}
comment_author: ${{ github.actor }}
github_repository: ${{ github.repository }}
ado_organization: ${{ secrets.ADO_PROJECTCOLLECTION }}
ado_project: ${{ secrets.ADO_PROJECT }}
backport_pipeline_id: ${{ secrets.BACKPORT_PIPELINEID }}
ado_build_pat: ${{ secrets.ADO_BUILDPAT }}
github_account_pat: ${{ secrets.SERVICEACCOUNT_PAT }}

22
.github/workflows/rebase-trigger.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
name: Rebase Trigger
on:
issue_comment:
types: [created]
jobs:
launchBackportBuild:
runs-on: ubuntu-latest
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '@vs-mobiletools-engineering-service2 rebase')
steps:
- uses: xamarin/rebase-bot-action@v1.0
with:
pull_request_url: ${{ github.event.issue.pull_request.url }}
comment_author: ${{ github.actor }}
github_repository: ${{ github.repository }}
ado_organization: ${{ secrets.ADO_PROJECTCOLLECTION }}
ado_project: ${{ secrets.ADO_PROJECT }}
rebase_pipeline_id: 13926
ado_build_pat: ${{ secrets.ADO_BUILDPAT }}
github_account_pat: ${{ secrets.SERVICEACCOUNT_PAT }}

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

@ -1346,4 +1346,4 @@ public string ParseDevOpsInputs(string nunitWhere)
}
return returnValue;
}
}

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

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<controls:TestContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue12848"
Title="Issue 12848">
<StackLayout>
<Label
Padding="12"
BackgroundColor="Black"
TextColor="White"
Text="Swipe the CarouselView to select the item numbered 2. Then, tap the Hide Button to hide the Carousel and the Show Button to show the Carousel again. If the position of the Carousel is the same as before hiding, the test has passed."/>
<CarouselView
AutomationId="TestCarouselView"
x:Name="carousel"
HorizontalOptions="Center"
ItemsSource="{Binding}">
<CarouselView.ItemTemplate>
<DataTemplate>
<Label
Text="{Binding}"
FontSize="96"
HorizontalTextAlignment="Center"/>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<Label
AutomationId="CarouselPosition"
Text="{Binding Source={x:Reference carousel}, Path=Position}"
HorizontalOptions="Center"/>
<Button
AutomationId="HideButton"
Text="Hide CarouselView"
Clicked="OnHideButtonClicked"/>
<Button
AutomationId="ShowButton"
Text="Show CarouselView"
Clicked="OnShowButtonClicked"/>
</StackLayout>
</controls:TestContentPage>

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

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.Forms.Core.UITests;
using NUnit.Framework;
#endif
namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 12848, "[Bug] CarouselView position resets when visibility toggled",
PlatformAffected.Android)]
public partial class Issue12848 : TestContentPage
{
protected override void Init()
{
#if APP
InitializeComponent();
BindingContext = new List<int> { 1, 2, 3 };
#endif
}
#if APP
void OnShowButtonClicked(object sender, EventArgs e)
{
carousel.IsVisible = true;
}
void OnHideButtonClicked(object sender, EventArgs e)
{
carousel.IsVisible = false;
}
#endif
#if UITEST
[Test]
public void Issue12848Test()
{
RunningApp.WaitForElement("TestCarouselView");
RunningApp.SwipeRightToLeft();
Assert.AreEqual(1, int.Parse(RunningApp.Query(q => q.Marked("CarouselPosition"))[0].Text));
RunningApp.Tap("HideButton");
RunningApp.Tap("ShowButton");
Assert.AreEqual(1, int.Parse(RunningApp.Query(q => q.Marked("CarouselPosition"))[0].Text));
RunningApp.Screenshot("Test passed");
}
#endif
}
}

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

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8" ?>
<local:TestContentPage
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"
Title="Test 13436" xmlns:local="using:Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue13436">
<StackLayout>
<Label
Padding="12"
BackgroundColor="Black"
TextColor="White"
Text="Without exceptions, the test has passed."/>
<CarouselView
x:Name="Carousel"
AutomationId="CarouselId"
HorizontalScrollBarVisibility="Never"
Loop="False"
VerticalOptions="Center">
<CarouselView.ItemsLayout>
<LinearItemsLayout
Orientation="Horizontal"
SnapPointsAlignment="Center"
SnapPointsType="MandatorySingle" />
</CarouselView.ItemsLayout>
<CarouselView.ItemTemplate>
<DataTemplate>
<Grid BackgroundColor="{Binding Color}">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
Margin="0,0,0,20"
Text="{Binding Name}"
TextColor="Black" />
<Label
Grid.Row="1"
Margin="0,0,0,20"
Text="{Binding Desc}"
TextColor="Black" />
</Grid>
</Grid>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</StackLayout>
</local:TestContentPage>

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

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif
namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 13436,
"[Bug] Java.Lang.IllegalArgumentException in CarouselView adjusting PeekAreaInsets in OnSizeAllocated using XF 5.0",
PlatformAffected.Android)]
public partial class Issue13436 : TestContentPage
{
public Issue13436()
{
#if APP
InitializeComponent();
#endif
}
#if APP
double _prevWidth;
protected override void OnAppearing()
{
base.OnAppearing();
Carousel.ItemsSource = new List<Issue13436Model>
{
new Issue13436Model
{
Name = "N1",
Desc = "D1",
Color = Color.Yellow
},
new Issue13436Model
{
Name = "N2",
Desc = "D2",
Color = Color.Orange
},
new Issue13436Model
{
Name = "N3",
Desc = "D3",
Color = Color.AliceBlue
}
};
}
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
if (Math.Abs(width - _prevWidth) < .1)
{
return;
}
_prevWidth = width;
Carousel.PeekAreaInsets = width * .15;
}
#endif
protected override void Init()
{
}
#if UITEST && __ANDROID__
[Test]
public void ChangePeekAreaInsetsInOnSizeAllocatedTest()
{
RunningApp.WaitForElement("CarouselId");
}
#endif
}
[Preserve(AllMembers = true)]
public class Issue13436Model
{
public string Name { get; set; }
public string Desc { get; set; }
public Color Color { get; set; }
public double Scale { get; set; }
}
}

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

@ -1684,6 +1684,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue12777.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11911.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11691.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12848.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12315.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12912.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12910.xaml.cs" />
@ -1706,6 +1707,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ShellFlyoutBackground.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellFlyoutContentOffest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellFlyoutContentWithZeroMargin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13436.xaml.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">
@ -2064,6 +2066,9 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue11691.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue12848.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue12315.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
@ -2103,6 +2108,9 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue12911.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue13436.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla27417Xaml.xaml">

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Color x:Key="notBlue">Red</Color>
<Color x:Key="AccentColor">#FF4B14</Color>
</ResourceDictionary>

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

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Xamarin.Forms.Build.Tasks;
@ -13,7 +14,7 @@ namespace Xamarin.Forms.Core.XamlC
{
public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
{
var module = context.Body.Method.Module;
var currentModule = context.Body.Method.Module;
var body = context.Body;
INode rootNode = node;
@ -22,7 +23,23 @@ namespace Xamarin.Forms.Core.XamlC
var rdNode = node.Parent as IElementNode;
var rootTargetPath = XamlCTask.GetPathForType(module, ((ILRootNode)rootNode).TypeReference);
var rootTargetPath = XamlCTask.GetPathForType(currentModule, ((ILRootNode)rootNode).TypeReference);
var module = currentModule;
string asmName = null;
if (value.Contains(";assembly="))
{
var parts = value.Split(new[] { ";assembly=" }, StringSplitOptions.RemoveEmptyEntries);
value = parts[0];
asmName = parts[1];
if (currentModule.Assembly.Name.Name != asmName)
{
var ar = currentModule.AssemblyReferences.FirstOrDefault(ar => ar.Name == asmName);
if (ar == null)
throw new BuildException(BuildExceptionCode.ResourceMissing, node, null, value);
module = currentModule.AssemblyResolver.Resolve(ar).MainModule;
}
}
var uri = new Uri(value, UriKind.Relative);
var resourcePath = ResourceDictionary.RDSourceTypeConverter.GetResourcePath(uri, rootTargetPath);
@ -36,25 +53,38 @@ namespace Xamarin.Forms.Core.XamlC
//abuse the converter, produce some side effect, but leave the stack untouched
//public void SetAndLoadSource(Uri value, string resourceID, Assembly assembly, System.Xml.IXmlLineInfo lineInfo)
foreach (var instruction in context.Variables[rdNode].LoadAs(module.GetTypeDefinition(resourceDictionaryType), module))
foreach (var instruction in context.Variables[rdNode].LoadAs(currentModule.GetTypeDefinition(resourceDictionaryType), currentModule))
yield return instruction;
//reappend assembly= in all cases, see other RD converter
if (!string.IsNullOrEmpty(asmName))
value = $"{value};assembly={asmName}";
else
value = $"{value};assembly={((ILRootNode)rootNode).TypeReference.Module.Assembly.Name.Name}";
foreach (var instruction in (new UriTypeConverter()).ConvertFromString(value, context, node))
yield return instruction; //the Uri
//keep the Uri for later
yield return Create(Dup);
var uriVarDef = new VariableDefinition(module.ImportReference(("System", "System", "Uri")));
var uriVarDef = new VariableDefinition(currentModule.ImportReference(("System", "System", "Uri")));
body.Variables.Add(uriVarDef);
yield return Create(Stloc, uriVarDef);
yield return Create(Ldstr, resourcePath); //resourcePath
yield return Create(Ldtoken, module.ImportReference(((ILRootNode)rootNode).TypeReference));
yield return Create(Call, module.ImportMethodReference(("mscorlib", "System", "Type"), methodName: "GetTypeFromHandle", parameterTypes: new[] { ("mscorlib", "System", "RuntimeTypeHandle") }, isStatic: true));
yield return Create(Call, module.ImportMethodReference(("mscorlib", "System.Reflection", "IntrospectionExtensions"), methodName: "GetTypeInfo", parameterTypes: new[] { ("mscorlib", "System", "Type") }, isStatic: true));
yield return Create(Callvirt, module.ImportPropertyGetterReference(("mscorlib", "System.Reflection", "TypeInfo"), propertyName: "Assembly", flatten: true));
if (!string.IsNullOrEmpty(asmName))
{
yield return Create(Ldstr, asmName);
yield return Create(Call, currentModule.ImportMethodReference(("mscorlib", "System.Reflection", "Assembly"), methodName: "Load", parameterTypes: new[] { ("mscorlib", "System", "String") }, isStatic: true));
}
else //we could use assembly.Load in the 'else' part too, but I don't want to change working code right now
{
yield return Create(Ldtoken, currentModule.ImportReference(((ILRootNode)rootNode).TypeReference));
yield return Create(Call, currentModule.ImportMethodReference(("mscorlib", "System", "Type"), methodName: "GetTypeFromHandle", parameterTypes: new[] { ("mscorlib", "System", "RuntimeTypeHandle") }, isStatic: true));
yield return Create(Call, currentModule.ImportMethodReference(("mscorlib", "System.Reflection", "IntrospectionExtensions"), methodName: "GetTypeInfo", parameterTypes: new[] { ("mscorlib", "System", "Type") }, isStatic: true));
yield return Create(Callvirt, currentModule.ImportPropertyGetterReference(("mscorlib", "System.Reflection", "TypeInfo"), propertyName: "Assembly", flatten: true));
}
foreach (var instruction in node.PushXmlLineInfo(context))
yield return instruction; //lineinfo
yield return Create(Callvirt, module.ImportMethodReference(resourceDictionaryType,
yield return Create(Callvirt, currentModule.ImportMethodReference(resourceDictionaryType,
methodName: "SetAndLoadSource",
parameterTypes: new[] { ("System", "System", "Uri"), ("mscorlib", "System", "String"), ("mscorlib", "System.Reflection", "Assembly"), ("System.Xml.ReaderWriter", "System.Xml", "IXmlLineInfo") }));
//ldloc the stored uri as return value

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

@ -377,7 +377,7 @@ namespace Xamarin.Forms
var element = LogicalChildren[i];
if (element is VisualElement c)
{
if (c.Bounds != startingLayout[i])
if (startingLayout.Count <= i || c.Bounds != startingLayout[i])
{
LayoutChanged?.Invoke(this, EventArgs.Empty);
return;

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

@ -175,7 +175,7 @@ namespace Xamarin.Forms
public int Count
{
get { return _innerDictionary.Count; }
get { return _innerDictionary.Count + (_mergedInstance?.Count ?? 0); }
}
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
@ -230,7 +230,7 @@ namespace Xamarin.Forms
public bool Remove(string key)
{
return _innerDictionary.Remove(key);
return _innerDictionary.Remove(key) || (_mergedInstance?.Remove(key) ?? false);
}
public ICollection<object> Values
@ -369,10 +369,23 @@ namespace Xamarin.Forms
var lineInfo = (serviceProvider.GetService(typeof(Xaml.IXmlLineInfoProvider)) as Xaml.IXmlLineInfoProvider)?.XmlLineInfo;
var rootTargetPath = XamlResourceIdAttribute.GetPathForType(rootObjectType);
var assembly = rootObjectType.GetTypeInfo().Assembly;
#if NETSTANDARD2_0
if (value.Contains(";assembly="))
{
var parts = value.Split(new[] { ";assembly=" }, StringSplitOptions.RemoveEmptyEntries);
value = parts[0];
var asmName = parts[1];
assembly = Assembly.Load(asmName);
}
#endif
var uri = new Uri(value, UriKind.Relative); //we don't want file:// uris, even if they start with '/'
var resourcePath = GetResourcePath(uri, rootTargetPath);
targetRD.SetAndLoadSource(uri, resourcePath, rootObjectType.GetTypeInfo().Assembly, lineInfo);
//Re-add the assembly= in all cases, so HotReload doesn't have to make assumptions
uri = new Uri($"{value};assembly={assembly.GetName().Name}", UriKind.Relative);
targetRD.SetAndLoadSource(uri, resourcePath, assembly, lineInfo);
return uri;
}

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

@ -9,6 +9,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml.Diagnostics;
namespace Xamarin.Forms
{
@ -493,6 +494,40 @@ namespace Xamarin.Forms
return _navigationManager.GoToAsync(state, animate, false);
}
public void AddLogicalChild(Element element)
{
if (element == null)
{
return;
}
if (_logicalChildren.Contains(element))
return;
_logicalChildren.Add(element);
element.Parent = this;
OnChildAdded(element);
VisualDiagnostics.OnChildAdded(this, element);
}
public void RemoveLogicalChild(Element element)
{
if (element == null)
{
return;
}
element.Parent = null;
if (!_logicalChildren.Contains(element))
return;
var oldLogicalIndex = _logicalChildren.IndexOf(element);
_logicalChildren.Remove(element);
OnChildRemoved(element, oldLogicalIndex);
VisualDiagnostics.OnChildRemoved(this, element, oldLogicalIndex);
}
public static readonly BindableProperty CurrentItemProperty =
BindableProperty.Create(nameof(CurrentItem), typeof(ShellItem), typeof(Shell), null, BindingMode.TwoWay,
propertyChanging: OnCurrentItemChanging,
@ -547,6 +582,11 @@ namespace Xamarin.Forms
ShellNavigationManager _navigationManager;
ShellFlyoutItemsManager _flyoutManager;
ObservableCollection<Element> _logicalChildren = new ObservableCollection<Element>();
internal override ReadOnlyCollection<Element> LogicalChildrenInternal =>
new ReadOnlyCollection<Element>(_logicalChildren);
public Shell()
{
_navigationManager = new ShellNavigationManager(this);
@ -566,6 +606,7 @@ namespace Xamarin.Forms
Navigation = new NavigationImpl(this);
Route = Routing.GenerateImplicitRoute("shell");
Initialize();
InternalChildren.CollectionChanged += OnInternalChildrenCollectionChanged;
}
void Initialize()
@ -784,38 +825,17 @@ namespace Xamarin.Forms
View FlyoutHeaderView
{
get => _flyoutHeaderView;
set
{
if (_flyoutHeaderView == value)
return;
if (_flyoutHeaderView != null)
OnChildRemoved(_flyoutHeaderView, -1);
_flyoutHeaderView = value;
if (_flyoutHeaderView != null)
OnChildAdded(_flyoutHeaderView);
}
}
View FlyoutFooterView
{
get => _flyoutFooterView;
set
{
if (_flyoutFooterView == value)
return;
if (_flyoutFooterView != null)
OnChildRemoved(_flyoutFooterView, -1);
_flyoutFooterView = value;
if (_flyoutFooterView != null)
OnChildAdded(_flyoutFooterView);
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (FlyoutHeaderView != null)
SetInheritedBindingContext(FlyoutHeaderView, BindingContext);
@ -871,18 +891,6 @@ namespace Xamarin.Forms
bool ValidDefaultShellItem(Element child) => !(child is MenuShellItem);
internal override IEnumerable<Element> ChildrenNotDrawnByThisElement
{
get
{
if (FlyoutHeaderView != null)
yield return FlyoutHeaderView;
if (FlyoutFooterView != null)
yield return FlyoutFooterView;
}
}
protected virtual void OnNavigated(ShellNavigatedEventArgs args)
{
}
@ -1094,6 +1102,18 @@ namespace Xamarin.Forms
return null;
}
void OnInternalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (Element element in e.NewItems)
AddLogicalChild(element);
if (e.OldItems != null)
foreach (Element element in e.OldItems)
RemoveLogicalChild(element);
}
void NotifyFlyoutBehaviorObservers()
{
if (CurrentItem == null || GetVisiblePage() == null)
@ -1106,56 +1126,44 @@ namespace Xamarin.Forms
void OnFlyoutHeaderChanged(object oldVal, object newVal)
{
if (FlyoutHeaderTemplate == null)
{
if (newVal is View newFlyoutHeader)
FlyoutHeaderView = newFlyoutHeader;
else
FlyoutHeaderView = null;
}
ShellTemplatedViewManager.OnViewDataChanged(
FlyoutHeaderTemplate,
ref _flyoutHeaderView,
newVal,
RemoveLogicalChild,
AddLogicalChild);
}
void OnFlyoutHeaderTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
{
if (newValue == null)
{
if (FlyoutHeader is View flyoutHeaderView)
FlyoutHeaderView = flyoutHeaderView;
else
FlyoutHeaderView = null;
}
else
{
var newHeaderView = (View)newValue.CreateContent(FlyoutHeader, this);
FlyoutHeaderView = newHeaderView;
}
ShellTemplatedViewManager.OnViewTemplateChanged(
newValue,
ref _flyoutHeaderView,
FlyoutHeader,
RemoveLogicalChild,
AddLogicalChild,
this);
}
void OnFlyoutFooterChanged(object oldVal, object newVal)
{
if (FlyoutFooterTemplate == null)
{
if (newVal is View newFlyoutFooter)
FlyoutFooterView = newFlyoutFooter;
else
FlyoutFooterView = null;
}
ShellTemplatedViewManager.OnViewDataChanged(
FlyoutFooterTemplate,
ref _flyoutFooterView,
newVal,
RemoveLogicalChild,
AddLogicalChild);
}
void OnFlyoutFooterTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
{
if (newValue == null)
{
if (FlyoutFooter is View flyoutFooterView)
FlyoutFooterView = flyoutFooterView;
else
FlyoutFooterView = null;
}
else
{
var newFooterView = (View)newValue.CreateContent(FlyoutFooter, this);
FlyoutFooterView = newFooterView;
}
ShellTemplatedViewManager.OnViewTemplateChanged(
newValue,
ref _flyoutFooterView,
FlyoutFooter,
RemoveLogicalChild,
AddLogicalChild,
this);
}
internal Element GetVisiblePage()
@ -1194,8 +1202,15 @@ namespace Xamarin.Forms
PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, new[] { FlyoutHeaderView });
if (FlyoutFooterView != null)
PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, new[] { FlyoutFooterView });
if (FlyoutContentView != null)
PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, new[] { FlyoutContentView });
}
protected override void LayoutChildren(double x, double y, double width, double height)
{
// Page by default tries to layout all logical children
// we don't want this behavior with shell
}
#region Shell Flyout Content
@ -1223,44 +1238,27 @@ namespace Xamarin.Forms
View FlyoutContentView
{
get => _flyoutContentView;
set
{
if (_flyoutContentView == value)
return;
if (_flyoutContentView != null)
OnChildRemoved(_flyoutContentView, -1);
_flyoutContentView = value;
if (_flyoutContentView != null)
OnChildAdded(_flyoutContentView);
}
}
void OnFlyoutContentChanged(object oldVal, object newVal)
{
if (FlyoutContentTemplate == null)
{
if (newVal is View newFlyoutContent)
FlyoutContentView = newFlyoutContent;
else
FlyoutContentView = null;
}
ShellTemplatedViewManager.OnViewDataChanged(
FlyoutContentTemplate,
ref _flyoutContentView,
newVal,
RemoveLogicalChild,
AddLogicalChild);
}
void OnFlyoutContentTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
{
if (newValue == null)
{
if (FlyoutContent is View flyoutContentView)
FlyoutContentView = flyoutContentView;
else
FlyoutContentView = null;
}
else
{
var newContentView = (View)newValue.CreateContent(FlyoutContent, this);
FlyoutContentView = newContentView;
}
ShellTemplatedViewManager.OnViewTemplateChanged(
newValue,
ref _flyoutContentView,
FlyoutContent,
RemoveLogicalChild,
AddLogicalChild,
this);
}
static void OnFlyoutContentChanging(BindableObject bindable, object oldValue, object newValue)

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

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms
{
public static class ShellTemplatedViewManager
{
public static void SetView(
ref View localView,
View newView,
Action<Element> OnChildRemoved,
Action<Element> OnChildAdded)
{
if (localView == newView)
return;
if (localView != null)
OnChildRemoved(localView);
localView = newView;
if (localView != null)
OnChildAdded(localView);
}
public static void OnViewDataChanged(
DataTemplate currentViewTemplate,
ref View localViewRef,
object newViewData,
Action<Element> OnChildRemoved,
Action<Element> OnChildAdded)
{
if (currentViewTemplate == null)
{
SetView(ref localViewRef,
newViewData as View,
OnChildRemoved,
OnChildAdded);
}
}
public static void OnViewTemplateChanged(
DataTemplate newViewTemplate,
ref View localViewRef,
object currentViewData,
Action<Element> OnChildRemoved,
Action<Element> OnChildAdded,
Shell shell)
{
View newContentView = currentViewData as View;
if (newViewTemplate != null)
{
newContentView = (View)newViewTemplate.CreateContent(newViewTemplate, shell);
}
SetView(ref localViewRef,
newContentView,
OnChildRemoved,
OnChildAdded);
}
}
}

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

@ -345,7 +345,7 @@ namespace Xamarin.Forms.Core.UnitTests
}
[Test]
public void CountDoesNotIncludeMerged()
public void CountDoesIncludeMerged()
{
var rd = new ResourceDictionary {
{"baz", "Baz"},
@ -353,7 +353,7 @@ namespace Xamarin.Forms.Core.UnitTests
};
rd.MergedWith = typeof(MyRD);
Assert.That(rd.Count, Is.EqualTo(2));
Assert.That(rd.Count, Is.EqualTo(4));
}
[Test]

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

@ -768,10 +768,10 @@ namespace Xamarin.Forms.Core.UnitTests
shell.FlyoutHeader = null;
shell.FlyoutHeader = layout;
Assert.True(shell.ChildrenNotDrawnByThisElement.Contains(layout));
Assert.True(shell.LogicalChildren.Contains(layout));
shell.FlyoutHeader = null;
Assert.False(shell.ChildrenNotDrawnByThisElement.Contains(layout));
Assert.False(shell.LogicalChildren.Contains(layout));
}

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

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" >
<Color x:Key="Color1">Chartreuse</Color>
</ResourceDictionary>

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

@ -0,0 +1,13 @@
<?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.Gh13209">
<StackLayout x:Name="Root">
<StackLayout.Resources>
<ResourceDictionary Source="../AppResources/RD13209.xaml"/>
</StackLayout.Resources>
<Rectangle x:Name="MyRect" BackgroundColor="{StaticResource Color1}" WidthRequest="100" HeightRequest="100" Margin="6"/>
<Label x:Name="MyLabel" WidthRequest="200" HeightRequest="30" Margin="6"/>
</StackLayout>
</ContentPage>

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

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
public partial class Gh13209 : ContentPage
{
public Gh13209() => InitializeComponent();
public Gh13209(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 RdWithSource(bool useCompiledXaml)
{
var layout = new Gh13209(useCompiledXaml);
Assert.That(layout.MyRect.BackgroundColor, Is.EqualTo(Color.Chartreuse));
Assert.That(layout.Root.Resources.Count, Is.EqualTo(1));
Assert.That(layout.Root.Resources.MergedDictionaries.Count, Is.EqualTo(0));
Assert.That(layout.Root.Resources["Color1"], Is.Not.Null);
Assert.That(layout.Root.Resources.Remove("Color1"), Is.True);
Assert.Throws<KeyNotFoundException>(() =>
{
var _ = layout.Root.Resources["Color1"];
});
}
}
}
}

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

@ -10,6 +10,8 @@
<ResourceDictionary Source="SharedResourceDictionary.xaml" x:Key="shortURI"/>
<ResourceDictionary Source="./AppResources/Colors.xaml" x:Key="Colors" />
<ResourceDictionary Source="./AppResources/CompiledColors.xaml" x:Key="CompiledColors" />
<ResourceDictionary Source="/AppResources/Colors.xaml;assembly=Xamarin.Forms.Xaml.UnitTests" x:Key="inCurrentAssembly" />
<ResourceDictionary Source="/AppResources.xaml;assembly=Xamarin.Forms.Controls" x:Key="inOtherAssembly" />
</ResourceDictionary>
</ContentPage.Resources>
<Label x:Name="label" Style="{StaticResource sharedfoo}"/>

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

@ -19,17 +19,8 @@ namespace Xamarin.Forms.Xaml.UnitTests
[TestFixture]
public class Tests
{
[SetUp]
public void Setup()
{
Device.PlatformServices = new MockPlatformServices();
}
[TearDown]
public void TearDown()
{
Device.PlatformServices = null;
}
[SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
[TearDown] public void TearDown() => Device.PlatformServices = null;
[TestCase(false), TestCase(true)]
public void RDWithSourceAreFound(bool useCompiledXaml)
@ -42,11 +33,11 @@ namespace Xamarin.Forms.Xaml.UnitTests
public void RelativeAndAbsoluteURI(bool useCompiledXaml)
{
var layout = new ResourceDictionaryWithSource(useCompiledXaml);
Assert.That(((ResourceDictionary)layout.Resources["relURI"]).Source, Is.EqualTo(new Uri("./SharedResourceDictionary.xaml", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["relURI"]).Source, Is.EqualTo(new Uri("./SharedResourceDictionary.xaml;assembly=Xamarin.Forms.Xaml.UnitTests", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["relURI"])["sharedfoo"], Is.TypeOf<Style>());
Assert.That(((ResourceDictionary)layout.Resources["absURI"]).Source, Is.EqualTo(new Uri("/SharedResourceDictionary.xaml", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["absURI"]).Source, Is.EqualTo(new Uri("/SharedResourceDictionary.xaml;assembly=Xamarin.Forms.Xaml.UnitTests", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["absURI"])["sharedfoo"], Is.TypeOf<Style>());
Assert.That(((ResourceDictionary)layout.Resources["shortURI"]).Source, Is.EqualTo(new Uri("SharedResourceDictionary.xaml", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["shortURI"]).Source, Is.EqualTo(new Uri("SharedResourceDictionary.xaml;assembly=Xamarin.Forms.Xaml.UnitTests", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["shortURI"])["sharedfoo"], Is.TypeOf<Style>());
Assert.That(((ResourceDictionary)layout.Resources["Colors"])["MediumGrayTextColor"], Is.TypeOf<Color>());
Assert.That(((ResourceDictionary)layout.Resources["CompiledColors"])["MediumGrayTextColor"], Is.TypeOf<Color>());
@ -73,6 +64,17 @@ namespace Xamarin.Forms.Xaml.UnitTests
var rd = Activator.CreateInstance(type);
Assert.That(rd as ResourceDictionary, Is.Not.Null);
}
[TestCase(false), TestCase(true)]
public void LoadResourcesWithAssembly(bool useCompiledXaml)
{
var layout = new ResourceDictionaryWithSource(useCompiledXaml);
Assert.That(((ResourceDictionary)layout.Resources["inCurrentAssembly"]).Source, Is.EqualTo(new Uri("/AppResources/Colors.xaml;assembly=Xamarin.Forms.Xaml.UnitTests", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["inCurrentAssembly"])["MediumGrayTextColor"], Is.TypeOf<Color>());
Assert.That(((ResourceDictionary)layout.Resources["inOtherAssembly"]).Source, Is.EqualTo(new Uri("/AppResources.xaml;assembly=Xamarin.Forms.Controls", UriKind.Relative)));
Assert.That(((ResourceDictionary)layout.Resources["inOtherAssembly"])["notBlue"], Is.TypeOf<Color>());
}
}
}
}

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

@ -334,21 +334,21 @@ namespace Xamarin.Forms.Platform.Android
void UpdateInitialPosition()
{
int position = 0;
int itemCount = 0;
int position;
if (Carousel.CurrentItem != null)
{
var carouselEnumerator = Carousel.ItemsSource.GetEnumerator();
var items = new List<object>();
while (carouselEnumerator.MoveNext())
{
if(carouselEnumerator.Current == Carousel.CurrentItem)
position = itemCount;
items.Add(carouselEnumerator.Current);
itemCount++;
}
position = items.IndexOf(Carousel.CurrentItem);
Carousel.Position = position;
}
else

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

@ -64,6 +64,16 @@ namespace Xamarin.Forms.Platform.Android
return id;
}
public override void OnViewRecycled(Java.Lang.Object holder)
{
if (holder is ElementViewHolder evh)
{
evh.Element = null;
}
base.OnViewRecycled(holder);
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
var item = _listItems[position];
@ -279,6 +289,7 @@ namespace Xamarin.Forms.Platform.Android
if (_element == value)
return;
_shell.RemoveLogicalChild(View);
if (_element != null && _element is BaseShellItem)
{
_element.ClearValue(AppCompat.Platform.RendererProperty);
@ -287,12 +298,12 @@ namespace Xamarin.Forms.Platform.Android
_element = value;
// Set Parent after binding context so parent binding context doesn't propagate to view
// Set binding context before calling AddLogicalChild so parent binding context doesn't propagate to view
View.BindingContext = value;
View.Parent = _shell;
if (_element != null)
{
_shell.AddLogicalChild(View);
FastRenderers.AutomationPropertiesProvider.AccessibilitySettingsChanged(_itemView, value);
_element.SetValue(AppCompat.Platform.RendererProperty, _itemView);
_element.PropertyChanged += OnElementPropertyChanged;

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

@ -21,7 +21,7 @@ namespace Xamarin.Forms.Platform.UWP
object _previousDataContext;
double _previousWidth;
FrameworkElement FrameworkElement { get; set; }
Shell _shell;
public ShellFlyoutItemRenderer()
{
this.DataContextChanged += OnDataContextChanged;
@ -46,6 +46,7 @@ namespace Xamarin.Forms.Platform.UWP
if (_content.BindingContext is INotifyPropertyChanged inpc)
inpc.PropertyChanged -= ShellElementPropertyChanged;
_shell?.RemoveLogicalChild(_content);
_content.Cleanup();
_content.MeasureInvalidated -= OnMeasureInvalidated;
_content.BindingContext = null;
@ -55,8 +56,8 @@ namespace Xamarin.Forms.Platform.UWP
var bo = (BindableObject)args.NewValue;
var element = bo as Element;
var shell = element?.FindParent<Shell>();
DataTemplate dataTemplate = (shell as IShellController)?.GetFlyoutItemDataTemplate(bo);
_shell = element?.FindParent<Shell>();
DataTemplate dataTemplate = (_shell as IShellController)?.GetFlyoutItemDataTemplate(bo);
if (bo != null)
bo.PropertyChanged += ShellElementPropertyChanged;
@ -65,7 +66,8 @@ namespace Xamarin.Forms.Platform.UWP
{
_content = (View)dataTemplate.CreateContent();
_content.BindingContext = bo;
_content.Parent = shell;
_shell.AddLogicalChild(_content);
_content.MeasureInvalidated += OnMeasureInvalidated;
IVisualElementRenderer renderer = Platform.CreateRenderer(_content);
Platform.SetRenderer(_content, renderer);

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

@ -88,9 +88,24 @@ namespace Xamarin.Forms.Platform.MacOS
if (Element == null || Control == null)
return _defaultIsAccessibilityElement;
// If the user hasn't set IsInAccessibleTree then just don't do anything
if (!Element.IsSet(AutomationProperties.IsInAccessibleTreeProperty))
return null;
#if __MOBILE__
if (!_defaultIsAccessibilityElement.HasValue)
_defaultIsAccessibilityElement = Control.IsAccessibilityElement;
{
// iOS sets the default value for IsAccessibilityElement late in the layout cycle
// But if we set it to false ourselves then that causes it to act like it's false
// from the docs:
// https://developer.apple.com/documentation/objectivec/nsobject/1615141-isaccessibilityelement
// The default value for this property is false unless the receiver is a standard UIKit control,
// in which case the value is true.
//
// So we just base the default on that logic
_defaultIsAccessibilityElement = Control.IsAccessibilityElement || Control is UIControl;
}
Control.IsAccessibilityElement = (bool)((bool?)Element.GetValue(AutomationProperties.IsInAccessibleTreeProperty) ?? _defaultIsAccessibilityElement);
#else

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

@ -95,8 +95,8 @@ namespace Xamarin.Forms.Platform.iOS
if (cell == null)
{
var view = (View)template.CreateContent(context, _context.Shell);
view.Parent = _context.Shell;
view.BindingContext = context;
view.Parent = _context.Shell;
cell = new UIContainerCell(cellId, view);
}
else

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

@ -33,7 +33,7 @@ namespace Xamarin.Forms.Platform.iOS
if (_cells != null)
{
foreach (var cell in _cells.Values)
cell.Disconnect();
cell.Disconnect(_context.Shell);
}
_cells = new Dictionary<Element, UIContainerCell>();
@ -57,7 +57,7 @@ namespace Xamarin.Forms.Platform.iOS
if (_cells != null)
{
foreach (var cell in _cells.Values)
cell.Disconnect();
cell.Disconnect(_context.Shell);
}
_cells = new Dictionary<Element, UIContainerCell>();
}
@ -120,18 +120,13 @@ namespace Xamarin.Forms.Platform.iOS
if (!_cells.TryGetValue(context, out cell))
{
var view = (View)template.CreateContent(context, _context.Shell);
cell = new UIContainerCell(cellId, view);
// Set Parent after binding context so parent binding context doesn't propagate to view
cell.BindingContext = context;
view.Parent = _context.Shell;
cell = new UIContainerCell(cellId, view, _context.Shell, context);
}
else
{
var view = _cells[context].View;
cell.Disconnect();
cell = new UIContainerCell(cellId, view);
cell.BindingContext = context;
cell = new UIContainerCell(cellId, view, _context.Shell, context);
}
cell.SetAccessibilityProperties(context);

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

@ -8,22 +8,32 @@ namespace Xamarin.Forms.Platform.iOS
{
IVisualElementRenderer _renderer;
object _bindingContext;
internal Action<UIContainerCell> ViewMeasureInvalidated { get; set; }
internal NSIndexPath IndexPath { get; set; }
internal UITableView TableView { get; set; }
public UIContainerCell(string cellId, View view) : base(UITableViewCellStyle.Default, cellId)
internal UIContainerCell(string cellId, View view, Shell shell, object context) : base(UITableViewCellStyle.Default, cellId)
{
View = view;
View.MeasureInvalidated += MeasureInvalidated;
SelectionStyle = UITableViewCellSelectionStyle.None;
_renderer = Platform.CreateRenderer(view);
Platform.SetRenderer(view, _renderer);
ContentView.AddSubview(_renderer.NativeView);
_renderer.NativeView.ClipsToBounds = true;
ContentView.ClipsToBounds = true;
BindingContext = context;
if (shell != null)
shell.AddLogicalChild(View);
}
public UIContainerCell(string cellId, View view) : this(cellId, view, null, null)
{
}
void MeasureInvalidated(object sender, System.EventArgs e)
@ -39,7 +49,7 @@ namespace Xamarin.Forms.Platform.iOS
TableView.ReloadRows(new[] { IndexPath }, UITableViewRowAnimation.Automatic);
}
internal void Disconnect()
internal void Disconnect(Shell shell = null)
{
ViewMeasureInvalidated = null;
View.MeasureInvalidated -= MeasureInvalidated;
@ -48,6 +58,9 @@ namespace Xamarin.Forms.Platform.iOS
_bindingContext = null;
Platform.SetRenderer(View, null);
if (shell != null)
shell.RemoveLogicalChild(shell);
View = null;
TableView = null;
}
@ -72,7 +85,6 @@ namespace Xamarin.Forms.Platform.iOS
baseShell2.PropertyChanged += OnElementPropertyChanged;
UpdateVisualState();
}
}
}

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

@ -223,12 +223,6 @@ namespace Xamarin.Forms.Platform.MacOS
_controlChanging?.Invoke(this, EventArgs.Empty);
#if __MOBILE__
_defaultColor = uiview.BackgroundColor;
// UIKit UIViews created via storyboard default IsAccessibilityElement to true, BUT
// UIViews created programmatically default IsAccessibilityElement to false.
// We need to default to true to allow all elements to be accessible by default and
// allow users to override this later via AutomationProperties.IsInAccessibleTree
uiview.IsAccessibilityElement = true;
#else
uiview.WantsLayer = true;
_defaultColor = uiview.Layer.BackgroundColor;