[UWP] [iOS] Verify selected items exist when updating native selections (#7902)
* Add selection synchronization tests * Resync native selection after ItemsSource is updated * Update native selection on iOS when changing ItemsSource; prevent crash when selection contains items not in source; fixes #6963 * Automated test
This commit is contained in:
Родитель
c137d02ed0
Коммит
88dfa6c2ed
|
@ -0,0 +1,41 @@
|
|||
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, 6963, "[Bug] CollectionView multiple pre-selection throws ArgumentOutOfRangeException when SelectedItems is bound to an ObservableCollection initialized inside the constructor.",
|
||||
PlatformAffected.iOS | PlatformAffected.UWP)]
|
||||
public class Issue6963 : TestNavigationPage
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
#if APP
|
||||
FlagTestHelpers.SetCollectionViewTestFlag();
|
||||
|
||||
PushAsync(new GalleryPages.CollectionViewGalleries.SelectionGalleries.SelectionSynchronization());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UITEST
|
||||
[Test]
|
||||
public void SelectedItemsNotInSourceDoesNotCrash()
|
||||
{
|
||||
// If this page didn't crash, then we're good
|
||||
RunningApp.WaitForElement("FirstLabel");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Issue5354.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue6963.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue7253.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue7621.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
new SelectionChangedCommandParameter(), Navigation),
|
||||
GalleryBuilder.NavButton("Filterable Single Selection", () =>
|
||||
new FilterSelection(), Navigation),
|
||||
GalleryBuilder.NavButton("Selection Synchronization", () =>
|
||||
new SelectionSynchronization(), Navigation),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<?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.SelectionSynchronization">
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<DataTemplate x:Key="TestTemplate">
|
||||
<Label Text="{Binding .}" Margin="0,3,0,3"></Label>
|
||||
</DataTemplate>
|
||||
<Style x:Key="CV" TargetType="CollectionView">
|
||||
<Setter Property="HeightRequest" Value="250"/>
|
||||
<Setter Property="ItemTemplate" Value="{StaticResource TestTemplate}"></Setter>
|
||||
<Setter Property="Margin" Value="5,2,5,5"></Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.Content>
|
||||
<ScrollView>
|
||||
<StackLayout>
|
||||
|
||||
<Label AutomationId="FirstLabel" Text="Set ItemsSource then SelectedItems"/>
|
||||
<Label Text="Should have items 2 and 3 selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Multiple"
|
||||
ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItems}">
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set SelectedItems then ItemsSource"/>
|
||||
<Label Text="Should have items 2 and 3 selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Multiple"
|
||||
SelectedItems="{Binding SelectedItems}" ItemsSource="{Binding Items}">
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set ItemsSource then SelectedItem"/>
|
||||
<Label Text="Should have item 2 selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Single"
|
||||
ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set SelectedItem then ItemsSource"/>
|
||||
<Label Text="Should have item 2 selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Single"
|
||||
SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding Items}">
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set SelectedItems (not in source) then ItemsSource"/>
|
||||
<Label Text="Should have nothing selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Multiple"
|
||||
SelectedItems="{Binding SelectedItemsNotInSource}" ItemsSource="{Binding Items}">
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set ItemsSource then SelectedItems (not in source)"/>
|
||||
<Label Text="Should have nothing selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Multiple"
|
||||
ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItemsNotInSource}" >
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set SelectedItem (not in source) then ItemsSource"/>
|
||||
<Label Text="Should have nothing selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Single"
|
||||
SelectedItem="{Binding SelectedItemNotInSource}" ItemsSource="{Binding Items}">
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Set ItemsSource then SelectedItem (not in source)"/>
|
||||
<Label Text="Should have nothing selected"/>
|
||||
<CollectionView Style="{StaticResource CV}" SelectionMode="Single"
|
||||
ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItemNotInSource}">
|
||||
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Switch out ItemSource for one with only some of the SelectedItems"/>
|
||||
<Button x:Name="SwitchSource" Text="Switch Source" Clicked="SwitchSourceClicked"/>
|
||||
<Label Text="After hitting the button, should only have Item 3 selected"/>
|
||||
<CollectionView x:Name="CVSwitchSource" Style="{StaticResource CV}" SelectionMode="Multiple"
|
||||
ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItems}">
|
||||
|
||||
</CollectionView>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
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 SelectionSynchronization : ContentPage
|
||||
{
|
||||
public SelectionSynchronization()
|
||||
{
|
||||
InitializeComponent();
|
||||
BindingContext = new SelectionSyncModel();
|
||||
}
|
||||
|
||||
void SwitchSourceClicked(object sender, EventArgs e)
|
||||
{
|
||||
var newSource = new List<string> { "Item -1", "Item 0", "Item 1", "Item 3", "Item 4", "Item 5" };
|
||||
CVSwitchSource.ItemsSource = newSource;
|
||||
}
|
||||
}
|
||||
|
||||
[Preserve(AllMembers = true)]
|
||||
public class SelectionSyncModel
|
||||
{
|
||||
public SelectionSyncModel()
|
||||
{
|
||||
Items = new List<string>() {
|
||||
"Item 1", "Item 2", "Item 3", "Item 4"
|
||||
};
|
||||
|
||||
SelectedItem = "Item 2";
|
||||
SelectedItems = new ObservableCollection<object> { "Item 3", "Item 2" };
|
||||
|
||||
SelectedItemNotInSource = "Foo";
|
||||
SelectedItemsNotInSource = new ObservableCollection<object> { "Foo", "Bar", "Baz" };
|
||||
}
|
||||
|
||||
public List<string> Items { get; set; }
|
||||
|
||||
public string SelectedItem { get; set; }
|
||||
public ObservableCollection<object> SelectedItems { get; set; }
|
||||
|
||||
public string SelectedItemNotInSource { get; set; }
|
||||
public ObservableCollection<object> SelectedItemsNotInSource { get; set; }
|
||||
}
|
||||
}
|
|
@ -67,6 +67,7 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
_ignoreNativeSelectionChange = true;
|
||||
|
||||
base.UpdateItemsSource();
|
||||
UpdateNativeSelection();
|
||||
|
||||
_ignoreNativeSelectionChange = false;
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
else
|
||||
{
|
||||
ListViewBase.SelectedItem =
|
||||
ListViewBase.Items.First(item =>
|
||||
ListViewBase.Items.FirstOrDefault(item =>
|
||||
{
|
||||
if (item is ItemTemplateContext itemPair)
|
||||
{
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
if (changedProperty.Is(ItemsView.ItemsSourceProperty))
|
||||
{
|
||||
ItemsViewController.UpdateItemsSource();
|
||||
UpdateItemsSource();
|
||||
}
|
||||
else if (changedProperty.Is(ItemsView.ItemTemplateProperty))
|
||||
{
|
||||
|
@ -128,6 +128,11 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
_layout.ItemsUpdatingScrollMode = Element.ItemsUpdatingScrollMode;
|
||||
}
|
||||
|
||||
protected virtual void UpdateItemsSource()
|
||||
{
|
||||
ItemsViewController.UpdateItemsSource();
|
||||
}
|
||||
|
||||
protected abstract ItemsViewController CreateController(ItemsView newElement, ItemsViewLayout layout);
|
||||
|
||||
NSIndexPath DetermineIndex(ScrollToRequestEventArgs args)
|
||||
|
|
|
@ -30,7 +30,11 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
internal void SelectItem(object selectedItem)
|
||||
{
|
||||
var index = GetIndexForItem(selectedItem);
|
||||
CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
|
||||
|
||||
if (index.Section > -1 && index.Item > -1)
|
||||
{
|
||||
CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
|
||||
}
|
||||
}
|
||||
|
||||
// Called by Forms to clear the native selection
|
||||
|
|
|
@ -19,11 +19,11 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
if (changedProperty.IsOneOf(SelectableItemsView.SelectedItemProperty, SelectableItemsView.SelectedItemsProperty))
|
||||
{
|
||||
SelectableItemsViewController.UpdateNativeSelection();
|
||||
UpdateNativeSelection();
|
||||
}
|
||||
else if (changedProperty.Is(SelectableItemsView.SelectionModeProperty))
|
||||
{
|
||||
SelectableItemsViewController.UpdateSelectionMode();
|
||||
UpdateSelectionMode();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,8 +41,24 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
return;
|
||||
}
|
||||
|
||||
SelectableItemsViewController.UpdateSelectionMode();
|
||||
UpdateSelectionMode();
|
||||
UpdateNativeSelection();
|
||||
}
|
||||
|
||||
protected virtual void UpdateNativeSelection()
|
||||
{
|
||||
SelectableItemsViewController.UpdateNativeSelection();
|
||||
}
|
||||
|
||||
protected virtual void UpdateSelectionMode()
|
||||
{
|
||||
SelectableItemsViewController.UpdateSelectionMode();
|
||||
}
|
||||
|
||||
protected override void UpdateItemsSource()
|
||||
{
|
||||
base.UpdateItemsSource();
|
||||
UpdateNativeSelection();
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче