-
-
Save NVentimiglia/2723411428cdbb72fac6 to your computer and use it in GitHub Desktop.
| // 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; | |
| }); | |
| } | |
| } |
@bmasis +1
@bmasis @barryculhane @luismts
Chuck this in SetItems (and you can remove ObservableSource). Should allow CollectionChanged to trigger (my issue at least was that it wasn't triggering for adding/removing elements from my ObservableCollection).
var itemsSourceINotifyCollectionChanged = ItemsSource as INotifyCollectionChanged;
if (itemsSourceINotifyCollectionChanged != null) {
itemsSourceINotifyCollectionChanged.CollectionChanged += CollectionChanged;
}
when i am using this itemStack , it does not have scrolling functionality and when i put that in scroll it does not scroll .
I encounter an problem on this line:
var isObs = ItemsSource.GetType().IsGenericType && ItemsSource.GetType().GetGenericTypeDefinition() == typeof(ObservableCollection<>);
I get the following error:
'Type' does not contain a definition for 'IsGenericType' and no extension method 'IsGenericType' accepting a first argument of type 'Type' could be found (are you missing a using directive or an assembly reference?)
Any idea?
None of this joins up, the original class and all the edits are incomplete unfortunately. Writing my own, thanks anyway.
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)
@eric-wieser error in ObservableSource = (IObservableReadOnlyCollection)o;
@fschwiet in setItem where do you get newValue?