Last active
October 31, 2021 21:06
-
-
Save NVentimiglia/2723411428cdbb72fac6 to your computer and use it in GitHub Desktop.
Need a Items control or a repeater for Xamarin ? Here you go. Bind to observable collections, define your data template and enjoy.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT License | |
// Nicholas Ventimiglia | |
// 2016-9-19 | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Collections.Specialized; | |
using System.Linq; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
/// <summary> | |
/// For repeated content without a automatic scroll view. Supports DataTemplates, Horizontal and Vertical layouts ! | |
/// </summary> | |
/// <remarks> | |
/// Warning TODO NO Visualization or Paging! Handle this in your view model. | |
/// </remarks> | |
public class ItemsStack : StackLayout | |
{ | |
#region BindAble | |
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create<ItemsStack, IEnumerable>(p => p.ItemsSource, default(IEnumerable<object>), BindingMode.TwoWay, null, ItemsSourceChanged); | |
public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create<ItemsStack, object>(p => p.SelectedItem, default(object), BindingMode.TwoWay, null, OnSelectedItemChanged); | |
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create<ItemsStack, DataTemplate>(p => p.ItemTemplate, default(DataTemplate)); | |
public event EventHandler<SelectedItemChangedEventArgs> SelectedItemChanged; | |
public IEnumerable ItemsSource | |
{ | |
get { return (IEnumerable)GetValue(ItemsSourceProperty); } | |
set { SetValue(ItemsSourceProperty, value); } | |
} | |
public object SelectedItem | |
{ | |
get { return GetValue(SelectedItemProperty); } | |
set { SetValue(SelectedItemProperty, value); } | |
} | |
public DataTemplate ItemTemplate | |
{ | |
get { return (DataTemplate)GetValue(ItemTemplateProperty); } | |
set { SetValue(ItemTemplateProperty, value); } | |
} | |
private static void ItemsSourceChanged(BindableObject bindable, IEnumerable oldValue, IEnumerable newValue) | |
{ | |
var itemsLayout = (ItemsStack)bindable; | |
itemsLayout.SetItems(); | |
} | |
private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue) | |
{ | |
var itemsView = (ItemsStack)bindable; | |
if (newValue == oldValue) | |
return; | |
itemsView.SetSelectedItem(newValue); | |
} | |
#endregion | |
#region item rendering | |
protected readonly ICommand ItemSelectedCommand; | |
protected virtual void SetItems() | |
{ | |
Children.Clear(); | |
if (ItemsSource == null) | |
{ | |
ObservableSource = null; | |
return; | |
} | |
foreach (var item in ItemsSource) | |
Children.Add(GetItemView(item)); | |
var isObs = ItemsSource.GetType().IsGenericType && ItemsSource.GetType().GetGenericTypeDefinition() == typeof(ObservableCollection<>); | |
if (isObs) | |
{ | |
ObservableSource = new ObservableCollection<object>(ItemsSource.Cast<object>()); | |
} | |
} | |
protected virtual View GetItemView(object item) | |
{ | |
var content = ItemTemplate.CreateContent(); | |
var view = content as View; | |
if (view == null) | |
return null; | |
view.BindingContext = item; | |
var gesture = new TapGestureRecognizer | |
{ | |
Command = ItemSelectedCommand, | |
CommandParameter = item | |
}; | |
AddGesture(view, gesture); | |
return view; | |
} | |
protected void AddGesture(View view, TapGestureRecognizer gesture) | |
{ | |
view.GestureRecognizers.Add(gesture); | |
var layout = view as Layout<View>; | |
if (layout == null) | |
return; | |
foreach (var child in layout.Children) | |
AddGesture(child, gesture); | |
} | |
protected virtual void SetSelectedItem(object selectedItem) | |
{ | |
var handler = SelectedItemChanged; | |
if (handler != null) | |
handler(this, new SelectedItemChangedEventArgs(selectedItem)); | |
} | |
ObservableCollection<object> _observableSource; | |
protected ObservableCollection<object> ObservableSource | |
{ | |
get { return _observableSource; } | |
set | |
{ | |
if (_observableSource != null) | |
{ | |
_observableSource.CollectionChanged -= CollectionChanged; | |
} | |
_observableSource = value; | |
if (_observableSource != null) | |
{ | |
_observableSource.CollectionChanged += CollectionChanged; | |
} | |
} | |
} | |
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | |
{ | |
switch (e.Action) | |
{ | |
case NotifyCollectionChangedAction.Add: | |
{ | |
int index = e.NewStartingIndex; | |
foreach (var item in e.NewItems) | |
Children.Insert(index++, GetItemView(item)); | |
} | |
break; | |
case NotifyCollectionChangedAction.Move: | |
{ | |
var item = ObservableSource[e.OldStartingIndex]; | |
Children.RemoveAt(e.OldStartingIndex); | |
Children.Insert(e.NewStartingIndex, GetItemView(item)); | |
} | |
break; | |
case NotifyCollectionChangedAction.Remove: | |
{ | |
Children.RemoveAt(e.OldStartingIndex); | |
} | |
break; | |
case NotifyCollectionChangedAction.Replace: | |
{ | |
Children.RemoveAt(e.OldStartingIndex); | |
Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex])); | |
} | |
break; | |
case NotifyCollectionChangedAction.Reset: | |
Children.Clear(); | |
foreach (var item in ItemsSource) | |
Children.Add(GetItemView(item)); | |
break; | |
} | |
} | |
#endregion | |
public ItemsStack() | |
{ | |
ItemSelectedCommand = new Command<object>(item => | |
{ | |
SelectedItem = item; | |
}); | |
} | |
} |
I uses a modified version of it for some extra feature, it was working all good until I upgraded my Xamarin.Forms version.
I gets null pointer exception over var content = ItemTemplate.CreateContent();
Anyone got the same issue or someone got a fix for it?
previous XF V - 2.3
current XF V - 4.2 (Forcefully needed to upgrade it cuz of iOS13 iPad issue with masterPage)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
None of this joins up, the original class and all the edits are incomplete unfortunately. Writing my own, thanks anyway.