Trying to work around the problem I fought all last week, I am attempting to make the Forms.ListView be able to handle the replacement of a cell on both iOS and Android. Again, Android works great but iOS doesn't. I've included sample source code (below) and a video to demonstrate the issue.
Description of the program:
- Single view, PCL Forms project, iOS and Android
- View is a ListView
- ListView's ItemsSource is a List of at least two Observable Collections (of an object w/ BindableProperties)
- ListView's Grouping is enabled
- ListView uses a ViewCell as DataTemplate (non-dynamic as opposed to last week's issue: https://forums.xamarin.com/discussion/55026/forms-listview-crashes-when-itemsource-has-item-removed-and-another-inserted#latestlast)
Environment:
- iOS 8.x and 9.x (device or simulator)
- Xamarin.Forms 1.5.1.6471
Actions:
- start program on device or simulator
- scroll down so the header of the second section is at the top of the screen
- make a selection in the bottom of the second section
Expected behavior:
App will replace the contents of the selected cell with the word "pizza"
Observed behavior:
- Android: App behaves as expected
- iOS: App scrolls ListView so that second section's header is in the middle of the screen.
Source code:
using System;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace CellDelete
{
public class App : Application
{
public class Item : BindableObject {
}
public class Item<T> : Item {
public static BindableProperty ValueProperty = BindableProperty.Create ("Value", typeof(T), typeof(Item<T>), default(T));
public T Value {
get { return (T)GetValue (ValueProperty); }
set { SetValue (ValueProperty, value); }
}
}
public class ItemGroup : ObservableCollection<Item> {
public string Title;
}
ObservableCollection<ItemGroup> groups;
ItemGroup strings, booleans;
ListView listView;
public App()
{
groups = new ObservableCollection<ItemGroup> ();
strings = new ItemGroup () { Title = "Strings" };
for (int i = 0; i < 10; i++)
strings.Add (new Item<string> () { Value = "strings[" + i + "]"} );
groups.Add (strings);
booleans = new ItemGroup () { Title = "Booleans" };
for (int i = 0; i < 10; i++)
booleans.Add (new Item<bool> () { Value = i%2==1 });
groups.Add (booleans);
listView = new ListView () {
ItemsSource = groups,
ItemTemplate = new DataTemplate (typeof(MyCell)),
IsGroupingEnabled = true,
HasUnevenRows = true,
};
listView.ItemSelected += OnItemSelected;
MainPage = new ContentPage () {
Content = listView,
Padding = new Thickness(0,20,0,0),
};
}
Item lastItem = new Item<string>() { Value = "pizza" };
public void OnItemSelected(object sender, SelectedItemChangedEventArgs e) {
listView.SelectedItem = null;
var item = e.SelectedItem as Item;
foreach (ItemGroup group in groups) {
if (group.Contains (item)) {
int index = group.IndexOf (item);
var tmpItem = item;
group.Remove (item);
group.Insert (index, lastItem);
System.Diagnostics.Debug.WriteLine ("removed:" + item + " for:" + lastItem);
lastItem = tmpItem;
return;
}
}
}
public class HeaderCell : ViewCell {
public HeaderCell() {
View = new Label { TextColor = Color.White, BackgroundColor = Color.Blue, HeightRequest=25 };
}
protected override void OnBindingContextChanged() {
base.OnBindingContextChanged ();
((Label)View).Text = ((ItemGroup)BindingContext).Title;
}
}
public class MyCell : ViewCell {
Label _label;
Switch _switch;
public MyCell() {
_label = new Label();
_label.SetBinding(Label.TextProperty,"Value");
_switch = new Switch();
_switch.SetBinding(Switch.IsToggledProperty,"Value");
View = new StackLayout() {
Children = { _label, _switch },
Orientation = StackOrientation.Vertical,
HeightRequest = 56,
};
}
protected override void OnBindingContextChanged() {
base.OnBindingContextChanged ();
View.BindingContext = BindingContext;
Type type = BindingContext?.GetType ();
_label.IsVisible = false;
_switch.IsVisible = false;
if (type == typeof(Item<string>) )
_label.IsVisible = true;
else if (type == typeof(Item<bool>))
_switch.IsVisible = true;
}
}
}
}