Reputation: 3393
I'm creating a replacement for ListView that allows the user to rearrange items in the UI.
But if the screen that is using this new ListView wants to know when items are rearranged, it will set the ItemsSource property to an ObservableCollection and then expect to be told when items are rearranged.
XAML:
<Layout>
<MyListView x:Name="MyList">
</Layout>
Code behind:
public class MyScreen
{
ObservableCollection<MyItem> Items = new ObservableCollection<MyItem>();
public MyScreen()
{
InitializeComponent();
MyList.ItemsSource = Items;
Items.CollectionChanged += OnItemsChanged;
}
}
MyListView.cs:
public class MyListView : AbsoluteLayout
{
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(MyListView), null);
public IEnumerable ItemsSource
{
get { return (IEnumerable)this.GetValue(ItemsSourceProperty); }
set { this.SetValue(ItemsSourceProperty, value); }
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (propertyName == nameof(ItemsSource))
{
foreach (object item in ItemsSource)
{
// Create a visual element, and set the BindingContext of that
// visual element to 'item'
}
}
}
public void RearrangeItem(int beforeIndex, int afterIndex)
{
if (ItemsSource is ObservableCollection<ARGH> collection)
{
collection.RemoveAt(...);
collection.Insert(...);
}
}
}
And you see the problem. I can't cast ItemsSource (an IEnumerable for consistency with Xamarin.Forms.ListView) to an ObservableCollection without knowing the type of objects in the collection, and the most straightforward way to do that would be to have the MyListView be generic as well, but since I'm creating the MyListView in XAML I can't do that.
I'm sure I could do some clever stuff with reflection where I ask the ItemsSource whether it derives from any ObservableCollection, and if so find the "RemoveAt" and "Insert" functions and call them using reflection, but I was hoping to find something simpler ...
Upvotes: 1
Views: 1051
Reputation: 3393
SLaks answer is the easiest if all you need is RemoveAt() and Insert(); for Move() this is what I ended up with so that I could call ObservableCollection<>.Move(oldIndex, newIndex) on an IEnumerable:
Type t = ItemsSource.GetType();
if (t.Name == "ObservableCollection`1")
{
MethodInfo method = t.GetRuntimeMethod("Move", new Type[] { typeof(Int32), typeof(Int32) });
method.Invoke(ItemsSource, new object[] { oldIndex, newIndex });
}
Upvotes: 0
Reputation: 888273
ObservableCollection<T>
implements the non-generic IList
interface, so you can cast to that and call RemoveAt()
& Insert()
passing object
arounds.
Upvotes: 1