Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Fix SelectedItem in Picker set to null when bound ItemsSource raises CollectionChanged reset event (#2032) #12177

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions Xamarin.Forms.Core.UnitTests/PickerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using NUnit.Framework;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.ComponentModel;

namespace Xamarin.Forms.Core.UnitTests
{
Expand Down Expand Up @@ -56,6 +59,38 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu
}
}

class ResettableCollection<T> : List<T>, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;

public void RaiseResetEvent()
{
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
class ResettableCollectionDataContext<T> : INotifyPropertyChanged
where T : class
{
public ResettableCollection<T> Items { get; set; }

T selectedItem;
public T SelectedItem
{
get => selectedItem; set
{
if (selectedItem == value)
{
return;
}

selectedItem = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}

public event PropertyChangedEventHandler PropertyChanged;
}

[Test]
public void TestSetSelectedIndexOnNullRows()
{
Expand Down Expand Up @@ -475,5 +510,45 @@ public void TestSelectedItemChangeSelectedIndex()
Assert.AreEqual(-1, picker.SelectedIndex);
Assert.AreEqual(null, picker.SelectedItem);
}

[Test]
public void TestItemsSourceRaisesResetEvent()
{
var bindingContext = new ResettableCollectionDataContext<PickerTestsContextFixture>()
{
Items = new ResettableCollection<PickerTestsContextFixture>()
{
new PickerTestsContextFixture("A", "AA"),
new PickerTestsContextFixture("B", "BB"),
new PickerTestsContextFixture("C", "CC"),
},
};
var picker = new Picker
{
BindingContext = bindingContext,
ItemDisplayBinding = new Binding("DisplayName"),
};
picker.SetBinding(Picker.ItemsSourceProperty, "Items");
picker.SetBinding(Picker.SelectedItemProperty, "SelectedItem");

bindingContext.SelectedItem = bindingContext.Items[1];
Assert.AreEqual(bindingContext.Items[1], picker.SelectedItem);
Assert.AreEqual(1, picker.SelectedIndex);

bindingContext.Items.RaiseResetEvent();

Assert.AreEqual(bindingContext.Items[1], picker.SelectedItem);
Assert.AreEqual(1, picker.SelectedIndex);

// check that if the selected item is removed as part of the "reset", then the selection
// is indeed set to null after the event is raised

bindingContext.Items.RemoveAt(1);
bindingContext.Items.RaiseResetEvent();

Assert.AreEqual(null, picker.SelectedItem);
Assert.AreEqual(-1, picker.SelectedIndex);

}
}
}
3 changes: 2 additions & 1 deletion Xamarin.Forms.Core/Picker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,11 @@ void ResetItems()
{
if (ItemsSource == null)
return;
var currentSelectedItem = SelectedItem;
((LockableObservableListWrapper)Items).InternalClear();
foreach (object item in ItemsSource)
((LockableObservableListWrapper)Items).InternalAdd(GetDisplayMember(item));
UpdateSelectedItem(SelectedIndex);
UpdateSelectedIndex(currentSelectedItem);
}

static void OnSelectedIndexChanged(object bindable, object oldValue, object newValue)
Expand Down