Don't force binding expression applications to queue (#6857)

* Don't force binding expression applications to queue; fixes #6609

* Make PropertyChangeBindingsOccurThroughMainThread test what it claims to test
This commit is contained in:
E.Z. Hart 2019-07-12 05:07:57 -06:00 коммит произвёл Stephane Delcroix
Родитель 8931dbd8ee
Коммит 5bc931f089
9 изменённых файлов: 191 добавлений и 5 удалений

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

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif
namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.CollectionView)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 6609, "[Bug, CollectionView] SelectionChangedCommand invoked before SelectedItem is set",
PlatformAffected.All)]
public class Issue6609 : TestNavigationPage
{
protected override void Init()
{
#if APP
FlagTestHelpers.SetCollectionViewTestFlag();
PushAsync(new GalleryPages.CollectionViewGalleries.SelectionGalleries.SelectionChangedCommandParameter());
#endif
}
#if UITEST
[Test]
public void SelectionChangedCommandParameterBoundToSelectedItemShouldMatchSelectedItem()
{
RunningApp.WaitForElement("Item 2");
RunningApp.Tap("Item 2");
RunningApp.WaitForElement("Success");
}
#endif
}
}

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

@ -13,6 +13,7 @@
<DependentUpon>Issue5046.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue6609.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellInsets.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewGrouping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5412.cs" />
@ -528,7 +529,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla39853.cs" />
<Compile Include="$(MSBuildThisFileDirectory)MultipleClipToBounds.cs" />
<Compile Include="$(MSBuildThisFileDirectory)_TemplateMarkup.xaml.cs">
<DependentUpon>_TemplateMarkup.xaml</DependentUpon>
<DependentUpon>_TemplateMarkup.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)PerformanceGallery\PerformanceDataManager.cs" />

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

@ -0,0 +1,40 @@
<?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.SelectionGalleries.SelectionChangedCommandParameter">
<ContentPage.Content>
<StackLayout>
<Label x:Name="Result" Text="Pending..."></Label>
<CollectionView ItemsSource="{Binding Items}"
SelectionMode="Single"
SelectionChangedCommandParameter="{Binding SelectedItem,Source={x:Reference MyCollectionView}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionChangedCommand="{Binding SelectionChangedCommand}"
x:Name="MyCollectionView">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10">
<Label Text="{Binding Text}"
d:Text="{Binding .}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<Label Text="{Binding Description}"
d:Text="Item description"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

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

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.SelectionGalleries
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SelectionChangedCommandParameter : ContentPage
{
public SelectionChangedCommandParameter()
{
InitializeComponent();
BindingContext = new ItemsViewModel(Result);
}
}
[Preserve(AllMembers = true)]
class Item
{
public string Id { get; set; }
public string Text { get; set; }
public string Description { get; set; }
}
[Preserve(AllMembers = true)]
class ItemsViewModel : INotifyPropertyChanged
{
public ObservableCollection<Item> Items { get; set; }
public Command LoadItemsCommand { get; set; }
Item _selectedItem;
readonly Label _result;
public event PropertyChangedEventHandler PropertyChanged;
public Item SelectedItem
{
get => _selectedItem;
set { _selectedItem = value; OnPropertyChanged(); }
}
public Command<Item> SelectionChangedCommand { get; }
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ItemsViewModel(Label result)
{
Items = new ObservableCollection<Item>();
for (int n = 0; n < 10; n++)
{
Items.Add(new Item { Id = n.ToString(), Text = $"Item {n}", Description = $"This is item {n}" });
}
SelectionChangedCommand = new Command<Item>(item =>
{
var fromParameter = item;
var fromSelectedItem = SelectedItem;
if (fromParameter != fromSelectedItem)
{
_result.Text = "Fail";
}
else
{
_result.Text = "Success";
}
});
_result = result;
}
}
}

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

@ -26,6 +26,8 @@
new SingleBoundSelection(), Navigation),
GalleryBuilder.NavButton("Multiple Selection, Bound", () =>
new MultipleBoundSelection(), Navigation),
GalleryBuilder.NavButton("SelectionChangedCommandParameter", () =>
new SelectionChangedCommandParameter(), Navigation),
}
}
};

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

@ -65,6 +65,9 @@
<EmbeddedResource Update="GalleryPages\CollectionViewGalleries\DataTemplateSelectorGallery.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\CollectionViewGalleries\SelectionGalleries\SelectionChangedCommandParameter.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\MapGallery.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>

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

@ -614,7 +614,7 @@ namespace Xamarin.Forms.Core.UnitTests
bindable.SetBinding(MockBindable.TextProperty, binding);
bool invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(a => invokeOnMainThreadWasCalled = true);
Device.PlatformServices = new MockPlatformServices(a => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
vm.Text = "updated";

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

@ -25,17 +25,19 @@ namespace Xamarin.Forms.Core.UnitTests
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync;
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc;
readonly bool useRealisticLabelMeasure;
readonly bool _isInvokeRequired;
public MockPlatformServices (Action<Action> invokeOnMainThread = null, Action<Uri> openUriAction = null,
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync = null,
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc = null,
bool useRealisticLabelMeasure = false)
bool useRealisticLabelMeasure = false, bool isInvokeRequired = false)
{
this.invokeOnMainThread = invokeOnMainThread;
this.openUriAction = openUriAction;
this.getStreamAsync = getStreamAsync;
this.getNativeSizeFunc = getNativeSizeFunc;
this.useRealisticLabelMeasure = useRealisticLabelMeasure;
_isInvokeRequired = isInvokeRequired;
}
static MD5CryptoServiceProvider checksum = new MD5CryptoServiceProvider ();
@ -85,7 +87,7 @@ namespace Xamarin.Forms.Core.UnitTests
public bool IsInvokeRequired
{
get { return false; }
get { return _isInvokeRequired; }
}
public string RuntimePlatform { get; set; }

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

@ -615,7 +615,14 @@ namespace Xamarin.Forms
}
}
Device.BeginInvokeOnMainThread(() => _expression.Apply());
if (Device.IsInvokeRequired)
{
Device.BeginInvokeOnMainThread(() => _expression.Apply());
}
else
{
_expression.Apply();
}
}
public bool TryGetValue(object source, out object value)