--- /dev/null
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using Tizen.Xamarin.Forms.Extension;
+using ElmSharp;
+
+namespace Tizen.Xamarin.Forms.Extension.Renderer
+{
+ public class GridViewCellRenderer : CellRenderer
+ {
+ public GridViewCellRenderer() : base("default")
+ {
+ IconPart = "elm.swallow.icon";
+ }
+
+ protected string IconPart { get; set; }
+
+ protected override EvasObject OnGetContent(Cell cell, string part)
+ {
+ if (part == IconPart)
+ {
+ var viewCell = cell as GridViewCell;
+ if (viewCell != null)
+ {
+ var renderer = Platform.GetOrCreateRenderer(viewCell.View);
+ int height = FindCellContentHeight(viewCell);
+ renderer.NativeView.MinimumHeight = height;
+ return renderer.NativeView;
+ }
+ return null;
+ }
+ return null;
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == "View")
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using Tizen.Xamarin.Forms.Extension;
+using ElmSharp;
+using NImage = Xamarin.Forms.Platform.Tizen.Native.Image;
+using TForms = Xamarin.Forms.Platform.Tizen.Forms;
+
+namespace Tizen.Xamarin.Forms.Extension.Renderer
+{
+ public class TypeGridViewCellRenderer<TCell> : CellRenderer where TCell : TypeGridViewCell
+ {
+ protected string IconPart { get; set; }
+
+ protected string EndPart { get; set; }
+
+ public TypeGridViewCellRenderer(string type) : base(type)
+ {
+ IconPart = "elm.swallow.icon";
+ EndPart = "elm.swallow.end";
+ }
+
+ protected override Span OnGetText(Cell cell, string part)
+ {
+ TCell type1Cell = cell as TCell;
+ return new Span()
+ {
+ Text = type1Cell.Text,
+ FontSize = -1
+ };
+ }
+
+ protected override EvasObject OnGetContent(Cell cell, string part)
+ {
+ if (part == IconPart)
+ {
+ TCell typeCell = cell as TCell;
+ var image = new NImage(TForms.Context.MainWindow);
+ var task = image.LoadFromImageSourceAsync(ImageSource.FromFile(typeCell.Content));
+ return image;
+ }
+
+ if (part == EndPart)
+ {
+ TCell typeCell = cell as TCell;
+ if (typeCell.IsBadgeVisible)
+ {
+ var badge = new ElmSharp.Layout(TForms.Context.MainWindow);
+ badge.SetTheme("layout", "badge", "default");
+ badge.SetPartText("elm.text", typeCell.BadgeCount.ToString());
+ return badge;
+ }
+ }
+ return null;
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == TypeGridViewCell.TextProperty.PropertyName)
+ {
+ return true;
+ }
+ else if (property == TypeGridViewCell.BadgeCountProperty.PropertyName || property == TypeGridViewCell.IsBadgeVisibleProperty.PropertyName)
+ {
+ var typeCell = cell as TCell;
+ EvasObject badge;
+ realizedView.TryGetValue(EndPart, out badge);
+ if (badge == null)
+ return true;
+
+ if (typeCell.IsBadgeVisible)
+ {
+ (badge as ElmSharp.Layout)?.SetPartText("elm.text", typeCell.BadgeCount.ToString());
+ badge.Show();
+ }
+ else
+ {
+ badge.Hide();
+ }
+ return false;
+ }
+ else if (property == TypeGridViewCell.ContentProperty.PropertyName)
+ {
+ EvasObject img;
+ var typeCell = cell as TCell;
+ realizedView.TryGetValue(IconPart, out img);
+ (img as NImage)?.LoadFromImageSourceAsync(typeCell.Content);
+ return false;
+ }
+ return false;
+ }
+ }
+
+ public class Type1GridViewCellRenderer : TypeGridViewCellRenderer<Type1GridViewCell>
+ {
+ public Type1GridViewCellRenderer() : base("type1")
+ {
+ }
+ }
+
+ public class Type2GridViewCellRenderer : TypeGridViewCellRenderer<Type2GridViewCell>
+ {
+ public Type2GridViewCellRenderer() : base("type2")
+ {
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Reflection;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using Tizen.Xamarin.Forms.Extension;
+using Tizen.Xamarin.Forms.Extension.Renderer;
+using ElmSharp;
+
+using ItemContext = Xamarin.Forms.Platform.Tizen.Native.ListView.ItemContext;
+using TForms = Xamarin.Forms.Platform.Tizen.Forms;
+
+[assembly: ExportRenderer(typeof(GridView), typeof(GridViewRenderer))]
+namespace Tizen.Xamarin.Forms.Extension.Renderer
+{
+ class GridViewRenderer : ViewRenderer<GridView, GenGrid>, IDisposable
+ {
+ readonly EventHandler<GrdiViewScrollToRequestedEventArgs> _scrollToRequested;
+ readonly NotifyCollectionChangedEventHandler _collectionChanged;
+
+ readonly IDictionary<Type, Type> _cellRendererMappingTable = new Dictionary<Type, Type>();
+ readonly IDictionary<Type, CellRenderer> _cellRendererCache = new Dictionary<Type, CellRenderer>();
+ readonly List<ItemContext> _itemContextList = new List<ItemContext>();
+
+ int _defaultItemHeight = 100;
+ int _defaultItemWidth = 100;
+
+ public GridViewRenderer()
+ {
+ _scrollToRequested = OnScrollToRequested;
+ _collectionChanged = OnCollectionChanged;
+
+ RegisterPropertyHandler(GridView.ColumnSpacingProperty, UpdateColumnSpacing);
+ RegisterPropertyHandler(GridView.RowSpacingProperty, UpdateRowSpacing);
+ RegisterPropertyHandler(GridView.ItemHeightProperty, UpdateItemHeight);
+ RegisterPropertyHandler(GridView.ItemWidthProperty, UpdateItemWidth);
+ RegisterPropertyHandler(GridView.SelectionModeProperty, UpdateSelectionMode);
+ RegisterPropertyHandler(GridView.CanSelectionMultipleProperty, UpdateSelectionMultiple);
+ RegisterPropertyHandler(GridView.IsHighlightEffectEnabledProperty, UpdateHighlight);
+ RegisterPropertyHandler(GridView.FillItemsProperty, UpdateFillItem);
+ RegisterPropertyHandler(GridView.OrientationProperty, UpdateOrientation);
+ RegisterPropertyHandler(GridView.ItemsSourceProperty, UpdateSource);
+
+ // It is not possible to get registered cell renderers from Xamarin.Forms.Core.
+ // The renderer that is used in GridView should be registered here.
+ _cellRendererMappingTable[typeof(GridViewCell)] = typeof(GridViewCellRenderer);
+ _cellRendererMappingTable[typeof(Type1GridViewCell)] = typeof(Type1GridViewCellRenderer);
+ _cellRendererMappingTable[typeof(Type2GridViewCell)] = typeof(Type2GridViewCellRenderer);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<GridView> e)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new GenGrid(TForms.Context.MainWindow));
+ }
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.ScrollToRequested -= _scrollToRequested;
+ e.OldElement.TemplatedItems.CollectionChanged -= _collectionChanged;
+ Control.ItemRealized -= ItemRealizedHandler;
+ Control.ItemUnrealized -= ItemUnrealizedHandler;
+ Control.ItemPressed -= ItemPressedHandler;
+ Control.ItemSelected -= ItemSelectedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.ScrollToRequested += _scrollToRequested;
+ e.NewElement.TemplatedItems.CollectionChanged += _collectionChanged;
+ Control.ItemRealized += ItemRealizedHandler;
+ Control.ItemUnrealized += ItemUnrealizedHandler;
+ Control.ItemPressed += ItemPressedHandler;
+ Control.ItemSelected += ItemSelectedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void UpdateColumnSpacing()
+ {
+ Control.WeightY = Element.ColumnSpacing;
+ }
+
+ void UpdateRowSpacing()
+ {
+ Control.WeightX = Element.RowSpacing;
+ }
+
+ void UpdateItemHeight()
+ {
+ if (Element.ItemHeight < 0)
+ {
+ Control.ItemHeight = _defaultItemHeight;
+ }
+ else
+ {
+ Control.ItemHeight = Element.ItemHeight;
+ }
+ }
+
+ void UpdateItemWidth()
+ {
+ if (Element.ItemWidth < 0)
+ {
+ Control.ItemWidth = _defaultItemWidth;
+ }
+ else
+ {
+ Control.ItemWidth = Element.ItemWidth;
+ }
+ }
+
+ void UpdateSelectionMode()
+ {
+ switch (Element.SelectionMode)
+ {
+ default:
+ case GridViewSelectionMode.Default:
+ Control.SelectionMode = GenGridSelectionMode.Default;
+ break;
+ case GridViewSelectionMode.Always:
+ Control.SelectionMode = GenGridSelectionMode.Always;
+ break;
+ case GridViewSelectionMode.DisplayOnly:
+ Control.SelectionMode = GenGridSelectionMode.DisplayOnly;
+ break;
+ case GridViewSelectionMode.None:
+ Control.SelectionMode = GenGridSelectionMode.None;
+ break;
+ }
+ }
+
+ void UpdateSelectionMultiple()
+ {
+ Control.MultipleSelection = Element.CanSelectionMultiple;
+ }
+
+ void UpdateHighlight()
+ {
+ Control.IsHighlight = Element.IsHighlightEffectEnabled;
+ }
+
+ void UpdateFillItem()
+ {
+ Control.FillItems = Element.FillItems;
+ }
+
+ void UpdateOrientation()
+ {
+ Control.IsHorizontal = (Element.Orientation == GridViewOrientation.Horizontal);
+ }
+
+ void ItemSelectedHandler(object sender, GenGridItemEventArgs e)
+ {
+ GenGridItem item = e.Item;
+
+ if (item != null)
+ {
+ int index = -1;
+ index = Element.TemplatedItems.IndexOf((item.Data as ItemContext).Cell);
+ Element.NotifyItemSelected(index);
+ }
+ }
+
+ void ItemPressedHandler(object sender, GenGridItemEventArgs e)
+ {
+ GenGridItem item = e.Item;
+
+ if (item != null)
+ {
+ int index = -1;
+ index = Element.TemplatedItems.IndexOf((item.Data as ItemContext).Cell);
+ Element.NotifyItemTapped(index);
+ }
+ }
+
+ void OnScrollToRequested(object sender, GrdiViewScrollToRequestedEventArgs e)
+ {
+ var index = Element.TemplatedItems.GetGlobalIndexOfItem(e.Item);
+ var cell = Element.TemplatedItems[index];
+ GenGridItem item = GetItemContext(cell)?.Item as GenGridItem;
+ if (item != null)
+ Control.ScrollTo(item, e.Position.ToNative(), e.IsAnimated);
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ Cell before = null;
+ if (e.NewStartingIndex + e.NewItems.Count < Element.TemplatedItems.Count)
+ {
+ before = Element.TemplatedItems[e.NewStartingIndex + e.NewItems.Count];
+ }
+ foreach (var data in e.NewItems)
+ {
+ AddItem(data as Cell, before);
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ foreach (var data in e.OldItems)
+ {
+ ItemContext itemXContext = GetItemContext(data as Cell);
+ itemXContext?.Item?.Delete();
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ UpdateSource();
+ }
+ }
+
+ void UpdateSource()
+ {
+ Control.Clear();
+ AddSource(Element.TemplatedItems);
+ }
+
+ void AddSource(IEnumerable source, Cell beforeCell = null)
+ {
+ foreach (var data in source)
+ {
+ AddItem(data as Cell, beforeCell);
+ }
+ }
+
+ void AddItem(Cell cell, Cell beforeCell = null)
+ {
+ CellRenderer renderer = GetCellRenderer(cell);
+
+ ItemContext itemContext = new ItemContext();
+ itemContext.Cell = cell;
+ itemContext.Renderer = renderer;
+
+ if (beforeCell != null)
+ {
+ GenGridItem beforeItem = GetItemContext(beforeCell)?.Item as GenGridItem;
+ itemContext.Item = Control.InsertBefore(renderer.Class, itemContext, beforeItem);
+ }
+ else
+ {
+ itemContext.Item = Control.Append(renderer.Class, itemContext);
+ }
+
+ cell.PropertyChanged += OnCellPropertyChanged;
+ (cell as ICellController).ForceUpdateSizeRequested += OnForceUpdateSizeRequested;
+ itemContext.Item.Deleted += ItemDeletedHandler;
+ _itemContextList.Add(itemContext);
+ }
+
+ void ItemDeletedHandler(object sender, EventArgs e)
+ {
+ ItemContext itemContext = (sender as GenGridItem).Data as ItemContext;
+ if (itemContext.Cell != null)
+ {
+ itemContext.Cell.PropertyChanged -= OnCellPropertyChanged;
+ (itemContext.Cell as ICellController).ForceUpdateSizeRequested -= OnForceUpdateSizeRequested;
+ }
+ _itemContextList.Remove(itemContext);
+ }
+
+ Type LookupHandlerType(Type viewType)
+ {
+ Type type = viewType;
+
+ while (true)
+ {
+ if (_cellRendererMappingTable.ContainsKey(type))
+ return _cellRendererMappingTable[type];
+
+ type = type.GetTypeInfo().BaseType;
+ if (type == null)
+ break;
+ }
+ return null;
+ }
+
+ CellRenderer GetCellRenderer(Cell cell, bool isGroup = false)
+ {
+ Type type = cell.GetType();
+ var cache = _cellRendererCache;
+ if (cache.ContainsKey(type))
+ return cache[type];
+
+ var handlerType = LookupHandlerType(type);
+
+ if (handlerType == null)
+ throw new ArgumentNullException("Unsupported cell type");
+
+ return cache[type] = (CellRenderer)Activator.CreateInstance(handlerType);
+ }
+
+ ItemContext GetItemContext(Cell cell)
+ {
+ if (cell == null)
+ {
+ return null;
+ }
+ else
+ {
+ return _itemContextList.Find(X => X.Cell == cell);
+ }
+ }
+
+ void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var cell = sender as Cell;
+ var context = GetItemContext(cell);
+ context.Renderer.SendCellPropertyChanged(cell, context.Item, e.PropertyName);
+ }
+
+ void OnForceUpdateSizeRequested(object sender, EventArgs e)
+ {
+ var cell = sender as Cell;
+ var itemContext = GetItemContext(cell);
+ if (itemContext.Item != null)
+ itemContext.Item.Update();
+ }
+
+ void ItemRealizedHandler(object sender, GenGridItemEventArgs evt)
+ {
+ ItemContext itemContext = (evt.Item.Data as ItemContext);
+
+ if (itemContext != null && itemContext.Cell != null)
+ {
+ (itemContext.Cell as ICellController).SendAppearing();
+ }
+ }
+
+ void ItemUnrealizedHandler(object sender, GenGridItemEventArgs evt)
+ {
+ ItemContext itemContext = (evt.Item.Data as ItemContext);
+ if (itemContext != null && itemContext.Cell != null)
+ {
+ (itemContext.Cell as ICellController).SendDisappearing();
+ itemContext.Renderer?.SendUnrealizedCell(itemContext.Cell);
+ }
+ }
+ }
+}
+
<ItemGroup>\r
<Compile Include="BackgroundRenderer.cs" />\r
<Compile Include="Cells\DoubleLabelCellRenderer.cs" />\r
+ <Compile Include="Cells\GridViewCellRenderer.cs" />\r
+ <Compile Include="Cells\GridViewTypeCellRenderer.cs" />\r
<Compile Include="Cells\MultilineCellRenderer.cs" />\r
<Compile Include="Cells\TypeCellRenderer.cs" />\r
<Compile Include="RadioButtonRenderer.cs" />\r
+ <Compile Include="GridViewRenderer.cs" />\r
<Compile Include="TizenExtension.cs" />\r
<Compile Include="Properties\AssemblyInfo.cs" />\r
</ItemGroup>\r
--- /dev/null
+using System;
+using Xamarin.Forms;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ /// <summary>
+ /// This interface is for internal use by platform renderers.
+ /// </summary>
+ public interface IGridVeiwCell { }
+
+ /// <summary>
+ /// GridViewCell containing a developer-defined View.
+ /// <remarks>
+ /// </remarks>
+ /// </summary>
+ /// <br>
+ /// Type1GridViewCell
+ /// <table border=2 style="text-align:center;border-collapse:collapse;">
+ /// <tr>
+ /// <th height = 100 width = 100 bgcolor ="#5cda5e">(View)</th>
+ /// <th height = 100 width = 100 bgcolor ="#5cda5e">(View)</th>
+ /// <th height = 100 width = 100 bgcolor ="#5cda5e">(View)</th>
+ /// </tr>
+ /// </table>
+ /// <br>
+ public class GridViewCell : ViewCell, IGridVeiwCell { }
+
+ /// <summary>
+ /// TypeGridViewCell containing Text, Content and Badge.
+ /// </summary>
+ /// <remarks>
+ /// TypeGridViewCell is a abstract class inherited from a cell.<br>
+ /// Type1GridViewCell Class and Type2GridViewCell Class are used to inherit this class.<br>
+ /// Properties are used equally and are only slightly different position.<br>
+ /// Each property is displayed in the specified position.<br>
+ /// The specified position is shown below.<br>
+ /// <br>
+ /// Type1GridViewCell
+ /// <table border=2 style="text-align:center;border-collapse:collapse;">
+ /// <tr>
+ /// <th height = 80 width = 100 bgcolor ="#5cd1d8"></th>
+ /// <th height = 80 width = 100 bgcolor ="#5cd1d8"></th>
+ /// <th height = 80 width = 100 bgcolor ="#5cd1d8"></th>
+ /// </tr>
+ /// <tr>
+ /// <th height = 20 width = 100 style =" vertical-align:top;">Text</th>
+ /// <th height = 20 width = 100 style =" vertical-align:top;">Text</th>
+ /// <th height = 20 width = 100 style =" vertical-align:top;">Text</th>
+ /// </tr>
+ /// </table>
+ /// <br>
+ /// Type2GridViewCell
+ /// <table border=2 style="text-align:bottom;border-collapse:collapse;">
+ /// <tr>
+ /// <th height = 100 width = 100 bgcolor ="#5cd1d8" style =" vertical-align:bottom;"> Text</th>
+ /// <th height = 100 width = 100 bgcolor ="#5cd1d8" style =" vertical-align:bottom;"> Text</th>
+ /// <th height = 100 width = 100 bgcolor ="#5cd1d8" style =" vertical-align:bottom;"> Text</th>
+ /// </tr>
+ /// </table>
+ /// Type2GridViewCell is almost same with Type1GridViewCell except for the position of Text.
+ /// </remarks>
+ public abstract class TypeGridViewCell : Cell, IGridVeiwCell
+ {
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(GridViewCell), "");
+
+ public static readonly BindableProperty ContentProperty = BindableProperty.Create("Content", typeof(string), typeof(GridViewCell), "");
+
+ public static readonly BindableProperty IsBadgeVisibleProperty = BindableProperty.Create("IsBadgeVisible", typeof(bool), typeof(GridViewCell), false);
+
+ public static readonly BindableProperty BadgeCountProperty = BindableProperty.Create("BadgeCount", typeof(int), typeof(GridViewCell), default(int));
+
+ /// <summary>
+ /// Gets or sets the the Text displayed as the content of Item.
+ /// </summary>
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the Comtent for Item.
+ /// </summary>
+ public string Content
+ {
+ get { return (string)GetValue(ContentProperty); }
+ set { SetValue(ContentProperty, value); }
+ }
+
+ /// <summary>
+ /// true or false, to indicate whether the badge is displayed on the right top side of the item
+ /// </summary>
+ public bool IsBadgeVisible
+ {
+ get { return (bool)GetValue(IsBadgeVisibleProperty); }
+ set { SetValue(IsBadgeVisibleProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the number that is displayed on the badge.
+ /// </summary>
+ public int BadgeCount
+ {
+ get { return (int)GetValue(BadgeCountProperty); }
+ set { SetValue(BadgeCountProperty, value); }
+ }
+ }
+ /// <summary>
+ /// Type1GridViewCell is a class inherited from a TypeGridViewCell.
+ /// </summary>
+ /// <remarks>
+ /// Each property is displayed in the specified position.<br>
+ /// The specified position is shown below.<br>
+ /// </remarks>
+ /// <br>
+ /// Type1GridViewCell
+ /// <table border=2 style="text-align:center;border-collapse:collapse;">
+ /// <tr>
+ /// <th height = 80 width = 100 bgcolor ="#5cd1d8"></th>
+ /// <th height = 80 width = 100 bgcolor ="#5cd1d8"></th>
+ /// <th height = 80 width = 100 bgcolor ="#5cd1d8"></th>
+ /// </tr>
+ /// <tr>
+ /// <th height = 20 width = 100 style =" vertical-align:top;">Text</th>
+ /// <th height = 20 width = 100 style =" vertical-align:top;">Text</th>
+ /// <th height = 20 width = 100 style =" vertical-align:top;">Text</th>
+ /// </tr>
+ /// </table>
+ public class Type1GridViewCell : TypeGridViewCell { }
+
+ /// <summary>
+ /// Type2GridViewCell is a class inherited from a TypeGridViewCell.
+ /// </summary>
+ /// <remarks>
+ /// Each property is displayed in the specified position.<br>
+ /// The specified position is shown below.<br>
+ /// </remarks>
+ /// <br>
+ /// Type2GridViewCell
+ /// <table border=2 style="text-align:bottom;border-collapse:collapse;">
+ /// <tr>
+ /// <th height = 100 width = 100 bgcolor ="#5cd1d8" style =" vertical-align:bottom;"> Text</th>
+ /// <th height = 100 width = 100 bgcolor ="#5cd1d8" style =" vertical-align:bottom;"> Text</th>
+ /// <th height = 100 width = 100 bgcolor ="#5cd1d8" style =" vertical-align:bottom;"> Text</th>
+ /// </tr>
+ /// </table>
+ public class Type2GridViewCell : TypeGridViewCell { }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+using System.Collections.Specialized;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ internal static class EnumerableExtensions
+ {
+ internal static int IndexOf<T>(this IEnumerable<T> enumerable, T item)
+ {
+ if (enumerable == null)
+ throw new ArgumentNullException("enumerable");
+
+ var i = 0;
+ foreach (T element in enumerable)
+ {
+ if (Equals(element, item))
+ return i;
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ public static NotifyCollectionChangedEventArgsEx WithCount(this NotifyCollectionChangedEventArgs e, int count)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ return new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Add, e.NewItems, e.NewStartingIndex);
+
+ case NotifyCollectionChangedAction.Remove:
+ return new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Remove, e.OldItems, e.OldStartingIndex);
+
+ case NotifyCollectionChangedAction.Move:
+ return new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Move, e.OldItems, e.NewStartingIndex, e.OldStartingIndex);
+
+ case NotifyCollectionChangedAction.Replace:
+ return new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Replace, e.NewItems, e.OldItems, e.OldStartingIndex);
+
+ default:
+ return new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Reset);
+ }
+ }
+
+ public static object CreateContent(this DataTemplate self, object item, BindableObject container)
+ {
+ var selector = self as DataTemplateSelector;
+ if (selector != null)
+ {
+ self = selector.SelectTemplate(item, container);
+ }
+ return self.CreateContent();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Diagnostics;
+using Xamarin.Forms;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ /// <summary>
+ /// An ItemsView that displays a collection of contents like a table.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// public class MyObject
+ /// {
+ /// public string Text { get; set; }
+ /// public string ImagePath { get; set; }
+ /// public bool IsBadgeVisible { get; set; }
+ /// public int Count { get; set; }
+ /// }
+ /// public class GridViewTest : ContentPage
+ /// {
+ /// public GridViewTest()
+ /// {
+ /// List<MyObject> MyData = new List<MyObject>();
+ /// for (int i = 0; i < 50; i++)
+ /// {
+ /// MyData.Add(new MyObject { Text = "AAAA", ImagePath = "1.png", IsBadgeVisible = false, Count = 0);
+ /// MyData.Add(new MyObject { Text = "BBBB", ImagePath = "1.png", IsBadgeVisible = false, Count = 0);
+ /// MyData.Add(new MyObject { Text = "CCCC", ImagePath = "1.png", IsBadgeVisible = false, Count = 0);
+ /// MyData.Add(new MyObject { Text = "DDDD", ImagePath = "1.png", IsBadgeVisible = false, Count = 0);
+ /// }
+ /// GridView view = new GridView
+ /// {
+ /// ItemColumn = new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) },
+ /// ItemRow = new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) },
+ /// SelectionMode = GridViewSelectionMode.Default,
+ /// CanSelectMultiple = true,
+ /// IsHighlightEffectEnabled = true,
+ /// ColumnSpacing = 0.5,
+ /// RowSpacing = 0.5,
+ /// ItemsSource = MyData,
+ /// ItemTemplate = new DataTemplate(() =>
+ /// {
+ /// Type1GridViewCell typeCell = new Type1GridViewCell();
+ /// typeCell.SetBinding(Type1GridViewCell.TextProperty, new Binding("Text"));
+ /// typeCell.SetBinding(Type1GridViewCell.ContentProperty, new Binding("ImagePath"));
+ /// typeCell.SetBinding(Type1GridViewCell.IsBadgeVisibleProperty, new Binding("IsBadgeVisible"));
+ /// typeCell.SetBinding(Type1GridViewCell.BadgeCountProperty, new Binding("Count"));
+ /// return typeCell;
+ /// })
+ /// };
+ /// view.ItemSelected += (sender, e) =>
+ /// {
+ /// MyObject item = (MyObject)e.SelectedItem;
+ /// item.Count++;
+ /// if (item.Count > 0)
+ /// {
+ /// item.IsBadgeVisible = true;
+ /// }
+ /// };
+ /// this.Content = new StackLayout
+ /// {
+ /// Children =
+ /// {
+ /// view
+ /// }
+ /// };
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public class GridView : ItemsView<Cell>, IListViewController
+ {
+ const int DefaultItemWidth = -1;
+ const int DefaultItemHeight = -1;
+
+ public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create("ColumnSpacing", typeof(double), typeof(GridView), default(double));
+
+ public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create("RowSpacing", typeof(double), typeof(GridView), default(double));
+
+ public static readonly BindableProperty ItemHeightProperty = BindableProperty.Create("ItemHeight", typeof(int), typeof(GridView), DefaultItemHeight);
+
+ public static readonly BindableProperty ItemWidthProperty = BindableProperty.Create("ItemWidth", typeof(int), typeof(GridView), DefaultItemWidth);
+
+ public static readonly BindableProperty SelectionModeProperty = BindableProperty.Create("SelectionMode", typeof(GridViewSelectionMode), typeof(GridView), GridViewSelectionMode.Default);
+
+ public static readonly BindableProperty CanSelectionMultipleProperty = BindableProperty.Create("CanSelectionMultiple", typeof(bool), typeof(GridView), false);
+
+ public static readonly BindableProperty IsHighlightEffectEnabledProperty = BindableProperty.Create("IsHighlight", typeof(bool), typeof(GridView), true);
+
+ public static readonly BindableProperty FillItemsProperty = BindableProperty.Create("FillItems", typeof(bool), typeof(GridView), default(bool));
+
+ public static readonly BindableProperty OrientationProperty = BindableProperty.Create("Orientation", typeof(GridViewOrientation), typeof(GridView), GridViewOrientation.Vertical);
+
+ public event EventHandler<ItemVisibilityEventArgs> ItemAppearing;
+
+ public event EventHandler<ItemVisibilityEventArgs> ItemDisappearing;
+
+ public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
+
+ public event EventHandler<TappedItemChangedEventArgs> ItemTapped;
+
+ public event EventHandler<GrdiViewScrollToRequestedEventArgs> ScrollToRequested;
+
+ /// <summary>
+ /// Identifiers of the direction for which the GridView expands while placing its items.
+ /// </summary>
+ public GridViewOrientation Orientation
+ {
+ get { return (GridViewOrientation)GetValue(OrientationProperty); }
+ set { SetValue(OrientationProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets a value that indicates whether the content fills the area for item.
+ /// By default, this value is false, the item's grid is not filled and the content is centered with the alignment.
+ /// </summary>
+ public bool FillItems
+ {
+ get { return (bool)GetValue(FillItemsProperty); }
+ set { SetValue(FillItemsProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets a value that indicates whether the multiple selection is allowed.
+ /// By default, the value is false.
+ /// </summary>
+ public bool CanSelectionMultiple
+ {
+ get { return (bool)GetValue(CanSelectionMultipleProperty); }
+ set { SetValue(CanSelectionMultipleProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets a value that indicates whether the highlight effect is enabled.
+ /// </summary>
+ public bool IsHighlightEffectEnabled
+ {
+ get { return (bool)GetValue(IsHighlightEffectEnabledProperty); }
+ set { SetValue(IsHighlightEffectEnabledProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the height of the items.
+ /// </summary>
+ public int ItemHeight
+ {
+ get { return (int)GetValue(ItemHeightProperty); }
+ set { SetValue(ItemHeightProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the width of the items.
+ /// </summary>
+ public int ItemWidth
+ {
+ get { return (int)GetValue(ItemWidthProperty); }
+ set { SetValue(ItemWidthProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets colum spacing of the GridView.
+ /// </summary>
+ public double ColumnSpacing
+ {
+ get { return (double)GetValue(ColumnSpacingProperty); }
+ set { SetValue(ColumnSpacingProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets row spacing of the GridView.
+ /// </summary>
+ public double RowSpacing
+ {
+ get { return (double)GetValue(RowSpacingProperty); }
+ set { SetValue(RowSpacingProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets select mode.
+ /// </summary>
+ public GridViewSelectionMode SelectionMode
+ {
+ get { return (GridViewSelectionMode)GetValue(SelectionModeProperty); }
+ set { SetValue(SelectionModeProperty, value); }
+ }
+
+ /// <summary>
+ /// Creates and initializes a new instance of the GridView class.
+ /// </summary>
+ public GridView() : base()
+ {
+ VerticalOptions = HorizontalOptions = LayoutOptions.FillAndExpand;
+ }
+
+ /// <summary>
+ /// Scrolls the GridView to the item.
+ /// </summary>
+ /// <param name="item">The item from your GridView.ItemsSource to scroll to.</param>
+ /// <param name="position">How the item should be positioned on screen.</param>
+ /// <param name="animated">Whether or not the scroll should be animated.</param>
+ public void ScrollTo(object item, ScrollToPosition position, bool animated)
+ {
+ if (!Enum.IsDefined(typeof(ScrollToPosition), position))
+ throw new ArgumentException("position is not a valid ScrollToPosition", "position");
+
+ var args = new GrdiViewScrollToRequestedEventArgs(item, position, animated);
+ OnScrollToRequested(args);
+ }
+
+ protected override Cell CreateDefault(object item)
+ {
+ return new Type1GridViewCell { Text = item.ToString() };
+ }
+
+ protected override void SetupContent(Cell content, int index)
+ {
+ base.SetupContent(content, index);
+ content.Parent = this;
+ }
+
+ protected override void UnhookContent(Cell content)
+ {
+ base.UnhookContent(content);
+ content.Parent = null;
+ }
+
+ internal void NotifyItemSelected(int index)
+ {
+ var cell = TemplatedItems[index];
+ if (cell != null)
+ {
+ ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs(cell.BindingContext));
+ }
+ }
+
+ internal void NotifyItemTapped(int index)
+ {
+ var cell = TemplatedItems[index];
+ if (cell != null)
+ {
+ //It can't invoke tapped event in Cell.
+ ItemTapped?.Invoke(this, new TappedItemChangedEventArgs(cell.BindingContext));
+ }
+ }
+
+ void OnScrollToRequested(GrdiViewScrollToRequestedEventArgs e)
+ {
+ EventHandler<GrdiViewScrollToRequestedEventArgs> handler = ScrollToRequested;
+ if (handler != null)
+ handler(this, e);
+ }
+
+ Cell IListViewController.CreateDefaultCell(object item)
+ {
+ return CreateDefault(item);
+ }
+
+ void IListViewController.SendCellAppearing(Cell cell)
+ {
+ EventHandler<ItemVisibilityEventArgs> handler = ItemAppearing;
+ if (handler != null)
+ handler(this, new ItemVisibilityEventArgs(cell.BindingContext));
+ }
+
+ void IListViewController.SendCellDisappearing(Cell cell)
+ {
+ EventHandler<ItemVisibilityEventArgs> handler = ItemDisappearing;
+ if (handler != null)
+ handler(this, new ItemVisibilityEventArgs(cell.BindingContext));
+ }
+
+ // Below Properties and methods are NOT used in GridView.
+ // For interacting with Cell(Xamarin.Form.Core) implementing IListViewController is needed,
+ // but these are unnecessary in GridView.
+ #region IListViewController
+
+ event EventHandler<ScrollToRequestedEventArgs> IListViewController.ScrollToRequested
+ {
+ add
+ {
+ Debug.WriteLine("");
+ }
+
+ remove
+ {
+ Debug.WriteLine("");
+ }
+ }
+
+ Element IListViewController.FooterElement { get; }
+
+ Element IListViewController.HeaderElement { get; }
+
+ bool IListViewController.RefreshAllowed { get; }
+
+ ListViewCachingStrategy IListViewController.CachingStrategy { get; }
+
+ void IListViewController.SendRefreshing()
+ {
+ throw new NotImplementedException();
+ }
+
+ string IListViewController.GetDisplayTextFromGroup(object cell)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IListViewController.NotifyRowTapped(int index, int inGroupIndex, Cell cell)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IListViewController.NotifyRowTapped(int index, Cell cell)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
--- /dev/null
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ /// <summary>
+ /// Enumeration of identifiers for item's select mode.
+ /// </summary>
+ public enum GridViewSelectionMode
+ {
+ /// <summary>
+ /// The item will only call their selection func and callback when first becoming selected.<br>
+ /// Any further clicks will do nothing, unless you set always select mode.
+ /// </summary>
+ Default,
+ /// <summary>
+ /// This means that, even if selected, every click calls the selected callbacks.
+ /// </summary>
+ Always,
+ /// <summary>
+ /// This turns off the ability to select the item entirely and they neither appear selected nor call selected callback functions.
+ /// </summary>
+ None,
+ /// <summary>
+ /// This applies the no-finger-size rule(Clickable objects should be bigger than human touch point device (your finger) for some touch or small screen devices.) with "None".
+ /// So it is enabled, the item can be shrink than predefined finger-size value. And the item will be updated.
+ /// </summary>
+ DisplayOnly
+ }
+
+ /// <summary>
+ /// Enumeration of identifiers of the direction for which the GridView expands while placing its items.
+ /// </summary>
+ public enum GridViewOrientation
+ {
+ /// <summary>
+ /// The GridView expands hotizontally.
+ /// </summary>
+ Horizontal,
+ /// <summary>
+ /// The GridView expands vertically.
+ /// </summary>
+ Vertical
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using Xamarin.Forms;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ /// <summary>
+ /// Arguments for the event that is raised when a scroll is requested.
+ /// </summary>
+ public class GrdiViewScrollToRequestedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// An element to scroll to.
+ /// </summary>
+ public object Item { get; private set; }
+
+ /// <summary>
+ /// An enumeration value that describes which part of an element to scroll to.
+ /// </summary>
+ public ScrollToPosition Position { get; private set; }
+
+ /// <summary>
+ /// Gets a value that tells whether the scroll operation should be animated.
+ /// </summary>
+ public bool IsAnimated { get; private set; }
+
+ internal GrdiViewScrollToRequestedEventArgs(object item, ScrollToPosition position, bool animated)
+ {
+ Item = item;
+ Position = position;
+ IsAnimated = animated;
+ }
+ }
+
+ /// <summary>
+ /// Arguments for the event that is raised when item is tapped.
+ /// </summary>
+ public class TappedItemChangedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// An element that is tapped.
+ /// </summary>
+ public object TappedItem { get; }
+
+ internal TappedItemChangedEventArgs(object item)
+ {
+ TappedItem = item;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Xamarin.Forms;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ /// <summary>
+ /// A base class for a view that contains a templated list of items.<br>
+ /// This class is for internal use by platfrom renderer.
+ /// </summary>
+ /// <typeparam name="TVisual">The type of visual that the ItemsView<TVisual>.ItemsSource items will be templated into.</typeparam>
+ public abstract class ItemsView<TVisual> : View, ITemplatedItemsView<TVisual> where TVisual : BindableObject
+ {
+ public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(ItemsView<TVisual>), null, propertyChanged: OnItemsSourceChanged);
+
+ public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(ItemsView<TVisual>), null);
+ /// <summary>
+ /// Gets or sets the source of items to template and display.
+ /// </summary>
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the DataTemplate to apply to the ItemsView<TVisual>.ItemsSource.
+ /// </summary>
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
+ IListProxy ITemplatedItemsView<TVisual>.ListProxy
+ {
+ get { return TemplatedItems.ListProxy; }
+ }
+
+ ITemplatedItemsList<TVisual> ITemplatedItemsView<TVisual>.TemplatedItems { get { return TemplatedItems; } }
+
+ internal TemplatedItemsList<ItemsView<TVisual>, TVisual> TemplatedItems { get; }
+
+ internal ItemsView()
+ {
+ TemplatedItems = new TemplatedItemsList<ItemsView<TVisual>, TVisual>(this, ItemsSourceProperty, ItemTemplateProperty);
+ }
+
+ TVisual IItemsView<TVisual>.CreateDefault(object item)
+ {
+ return CreateDefault(item);
+ }
+
+ protected abstract TVisual CreateDefault(object item);
+
+ protected virtual void SetupContent(TVisual content, int index)
+ {
+ }
+
+ protected virtual void UnhookContent(TVisual content)
+ {
+ }
+
+ void IItemsView<TVisual>.SetupContent(TVisual content, int index)
+ {
+ SetupContent(content, index);
+ }
+
+ void IItemsView<TVisual>.UnhookContent(TVisual content)
+ {
+ UnhookContent(content);
+ }
+
+ static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var element = newValue as Element;
+ if (element == null)
+ return;
+ element.Parent = (Element)bindable;
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using Xamarin.Forms;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ internal sealed class ListProxy : IReadOnlyList<object>, IListProxy, INotifyCollectionChanged
+ {
+ readonly ICollection _collection;
+ readonly IList _list;
+ readonly int _windowSize;
+
+ IEnumerator _enumerator;
+ int _enumeratorIndex;
+
+ bool _finished;
+ HashSet<int> _indexesCounted;
+
+ Dictionary<int, object> _items;
+ int _version;
+
+ int _windowIndex;
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+ public event EventHandler CountChanged;
+
+ public IEnumerable ProxiedEnumerable { get; }
+
+ public int Count
+ {
+ get
+ {
+ if (_collection != null)
+ return _collection.Count;
+
+ EnsureWindowCreated();
+
+ if (_indexesCounted != null)
+ return _indexesCounted.Count;
+
+ return 0;
+ }
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ object value;
+ if (!TryGetValue(index, out value))
+ throw new ArgumentOutOfRangeException("index");
+
+ return value;
+ }
+ }
+
+ internal ListProxy(IEnumerable enumerable, int windowSize = int.MaxValue)
+ {
+ _windowSize = windowSize;
+
+ ProxiedEnumerable = enumerable;
+ _collection = enumerable as ICollection;
+
+ if (_collection == null && enumerable is IReadOnlyCollection<object>)
+ _collection = new ReadOnlyListAdapter((IReadOnlyCollection<object>)enumerable);
+
+ _list = enumerable as IList;
+ if (_list == null && enumerable is IReadOnlyList<object>)
+ _list = new ReadOnlyListAdapter((IReadOnlyList<object>)enumerable);
+
+ var changed = enumerable as INotifyCollectionChanged;
+ if (changed != null)
+ new WeakNotifyProxy(this, changed);
+ }
+
+ public IEnumerator<object> GetEnumerator()
+ {
+ return new ProxyEnumerator(this);
+ }
+
+ public bool Contains(object item)
+ {
+ if (_list != null)
+ return _list.Contains(item);
+
+ EnsureWindowCreated();
+
+ if (_items != null)
+ return _items.Values.Contains(item);
+
+ return false;
+ }
+
+ public int IndexOf(object item)
+ {
+ if (_list != null)
+ return _list.IndexOf(item);
+
+ EnsureWindowCreated();
+
+ if (_items != null)
+ {
+ foreach (KeyValuePair<int, object> kvp in _items)
+ {
+ if (Equals(kvp.Value, item))
+ return kvp.Key;
+ }
+ }
+
+ return -1;
+ }
+
+ public void Clear()
+ {
+ _version++;
+ _finished = false;
+ _windowIndex = 0;
+ _enumeratorIndex = 0;
+
+ if (_enumerator != null)
+ {
+ var dispose = _enumerator as IDisposable;
+ if (dispose != null)
+ dispose.Dispose();
+
+ _enumerator = null;
+ }
+
+ if (_items != null)
+ _items.Clear();
+ if (_indexesCounted != null)
+ _indexesCounted.Clear();
+
+ OnCountChanged();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ void ClearRange(int index, int clearCount)
+ {
+ if (_items == null)
+ return;
+
+ for (int i = index; i < index + clearCount; i++)
+ _items.Remove(i);
+ }
+
+ bool CountIndex(int index)
+ {
+ if (_collection != null)
+ return false;
+
+ // A collection is used in case TryGetValue is called out of order.
+ if (_indexesCounted == null)
+ _indexesCounted = new HashSet<int>();
+
+ if (_indexesCounted.Contains(index))
+ return false;
+
+ _indexesCounted.Add(index);
+ return true;
+ }
+
+ void EnsureWindowCreated()
+ {
+ if (_items != null && _items.Count > 0)
+ return;
+
+ object value;
+ TryGetValue(0, out value);
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Action action;
+ if (_list == null)
+ {
+ action = Clear;
+ }
+ else
+ {
+ action = () =>
+ {
+ _version++;
+ OnCollectionChanged(e);
+ };
+ }
+ e = e.WithCount(Count);
+ action();
+ }
+
+ void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler changed = CollectionChanged;
+ changed?.Invoke(this, e);
+ }
+
+ void OnCountChanged()
+ {
+ EventHandler changed = CountChanged;
+ changed?.Invoke(this, EventArgs.Empty);
+ }
+
+ bool TryGetValue(int index, out object value)
+ {
+ value = null;
+
+ if (_list != null)
+ {
+ object indexedValue = null;
+ var inRange = false;
+ Action getFromList = () =>
+ {
+ if (index >= _list.Count)
+ return;
+
+ indexedValue = _list[index];
+ inRange = true;
+ };
+
+ getFromList();
+
+ value = indexedValue;
+ return inRange;
+ }
+
+ if (_collection != null && index >= _collection.Count)
+ return false;
+ if (_items != null)
+ {
+ bool found = _items.TryGetValue(index, out value);
+ if (found || _finished)
+ return found;
+ }
+
+ if (index >= _windowIndex + _windowSize)
+ {
+ int newIndex = index - _windowSize / 2;
+ ClearRange(_windowIndex, newIndex - _windowIndex);
+ _windowIndex = newIndex;
+ }
+ else if (index < _windowIndex)
+ {
+ int clearIndex = _windowIndex;
+ int clearSize = _windowSize;
+ if (clearIndex <= index + clearSize)
+ {
+ int diff = index + clearSize - clearIndex;
+ clearIndex += diff + 1;
+ clearSize -= diff;
+ }
+
+ ClearRange(clearIndex, clearSize);
+ _windowIndex = 0;
+
+ var dispose = _enumerator as IDisposable;
+ if (dispose != null)
+ dispose.Dispose();
+
+ _enumerator = null;
+ _enumeratorIndex = 0;
+ }
+
+ if (_enumerator == null)
+ _enumerator = ProxiedEnumerable.GetEnumerator();
+ if (_items == null)
+ _items = new Dictionary<int, object>();
+
+ var countChanged = false;
+ int end = _windowIndex + _windowSize;
+
+ for (; _enumeratorIndex < end; _enumeratorIndex++)
+ {
+ var moved = false;
+ Action move = () =>
+ {
+ try
+ {
+ moved = _enumerator.MoveNext();
+ }
+ catch (InvalidOperationException ioex)
+ {
+ throw new InvalidOperationException("You must call UpdateNonNotifyingList() after updating a list that does not implement INotifyCollectionChanged", ioex);
+ }
+
+ if (!moved)
+ {
+ var dispose = _enumerator as IDisposable;
+ if (dispose != null)
+ dispose.Dispose();
+
+ _enumerator = null;
+ _enumeratorIndex = 0;
+ _finished = true;
+ }
+ };
+
+ move();
+
+ if (!moved)
+ break;
+
+ if (CountIndex(_enumeratorIndex))
+ countChanged = true;
+
+ if (_enumeratorIndex >= _windowIndex)
+ _items.Add(_enumeratorIndex, _enumerator.Current);
+ }
+
+ if (countChanged)
+ OnCountChanged();
+
+ return _items.TryGetValue(index, out value);
+ }
+
+ class WeakNotifyProxy
+ {
+ readonly WeakReference<INotifyCollectionChanged> _weakCollection;
+ readonly WeakReference<ListProxy> _weakProxy;
+
+ public WeakNotifyProxy(ListProxy proxy, INotifyCollectionChanged incc)
+ {
+ incc.CollectionChanged += OnCollectionChanged;
+
+ _weakProxy = new WeakReference<ListProxy>(proxy);
+ _weakCollection = new WeakReference<INotifyCollectionChanged>(incc);
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ ListProxy proxy;
+ if (!_weakProxy.TryGetTarget(out proxy))
+ {
+ INotifyCollectionChanged collection;
+ if (_weakCollection.TryGetTarget(out collection))
+ collection.CollectionChanged -= OnCollectionChanged;
+
+ return;
+ }
+
+ proxy.OnCollectionChanged(sender, e);
+ }
+ }
+
+ class ProxyEnumerator : IEnumerator<object>
+ {
+ readonly ListProxy _proxy;
+ readonly int _version;
+
+ int _index;
+
+ public ProxyEnumerator(ListProxy proxy)
+ {
+ _proxy = proxy;
+ _version = proxy._version;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public bool MoveNext()
+ {
+ if (_proxy._version != _version)
+ throw new InvalidOperationException();
+
+ object value;
+ bool next = _proxy.TryGetValue(_index++, out value);
+ if (next)
+ Current = value;
+
+ return next;
+ }
+
+ public void Reset()
+ {
+ _index = 0;
+ Current = null;
+ }
+
+ public object Current { get; private set; }
+ }
+
+ // Below properties and methods are NOT used in GridView.
+ // For porting ItemsView from Xamarin.Forms, implementing IListProxy(Xamarin.Form) is needed for type checking,
+ // but these are unnecessary in GridView.
+ #region IListProxy
+
+ bool ICollection.IsSynchronized
+ {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ object IList.this[int index]
+ {
+ get { return this[index]; }
+ set { throw new NotSupportedException(); }
+ }
+
+ bool IList.IsReadOnly
+ {
+ get { return true; }
+ }
+
+ bool IList.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ int IList.Add(object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Remove(object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Insert(int index, object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+ }
+}
+
[assembly: AssemblyTrademark("")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+
+[assembly: InternalsVisibleTo("Tizen.Xamarin.Forms.Extension.Renderer")]
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ internal sealed class ReadOnlyListAdapter : IList
+ {
+ readonly IReadOnlyCollection<object> _collection;
+ readonly IReadOnlyList<object> _list;
+
+ public ReadOnlyListAdapter(IReadOnlyList<object> list)
+ {
+ _list = list;
+ _collection = list;
+ }
+
+ public ReadOnlyListAdapter(IReadOnlyCollection<object> collection)
+ {
+ _collection = collection;
+ }
+
+ // Below properties and methods are NOT used in GridView.
+ // For porting ItemsView from Xamarin.Forms, implementing IList is needed for type checking,
+ // but these are unnecessary in GridView.
+ #region IList
+
+ public void CopyTo(Array array, int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ public int Count
+ {
+ get { return _collection.Count; }
+ }
+
+ public bool IsSynchronized
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _collection.GetEnumerator();
+ }
+
+ public int Add(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Clear()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool Contains(object value)
+ {
+ return _list.Contains(value);
+ }
+
+ public int IndexOf(object value)
+ {
+ return _list.IndexOf(value);
+ }
+
+ public void Insert(int index, object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsFixedSize
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public object this[int index]
+ {
+ get { return _list[index]; }
+ set { throw new NotImplementedException(); }
+ }
+
+ public void Remove(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RemoveAt(int index)
+ {
+ throw new NotImplementedException();
+ }
+ #endregion
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+
+namespace Tizen.Xamarin.Forms.Extension
+{
+ internal sealed class TemplatedItemsList<TView, TItem> : BindableObject, ITemplatedItemsList<TItem>, IList, IDisposable
+ where TView : BindableObject, IItemsView<TItem>
+ where TItem : BindableObject
+ {
+ internal static readonly BindablePropertyKey ListProxyPropertyKey = BindableProperty.CreateReadOnly("ListProxy", typeof(ListProxy), typeof(TemplatedItemsList<TView, TItem>), null,
+ propertyChanged: OnListProxyChanged);
+
+ static readonly BindableProperty IndexProperty = BindableProperty.Create("Index", typeof(int), typeof(TItem), -1);
+
+ readonly BindableProperty _itemSourceProperty;
+ readonly BindableProperty _itemTemplateProperty;
+
+ readonly TView _itemsView;
+
+ readonly List<TItem> _templatedObjects = new List<TItem>();
+
+ bool _disposed;
+
+ internal TemplatedItemsList(TView itemsView, BindableProperty itemSourceProperty, BindableProperty itemTemplateProperty)
+ {
+ if (itemsView == null)
+ throw new ArgumentNullException("itemsView");
+ if (itemSourceProperty == null)
+ throw new ArgumentNullException("itemSourceProperty");
+ if (itemTemplateProperty == null)
+ throw new ArgumentNullException("itemTemplateProperty");
+
+ _itemsView = itemsView;
+ _itemsView.PropertyChanged += BindableOnPropertyChanged;
+
+ _itemSourceProperty = itemSourceProperty;
+ _itemTemplateProperty = itemTemplateProperty;
+
+ IEnumerable source = GetItemsViewSource();
+ if (source != null)
+ ListProxy = new ListProxy(source);
+ else
+ ListProxy = new ListProxy(new object[0]);
+ }
+
+ internal TemplatedItemsList(TemplatedItemsList<TView, TItem> parent, IEnumerable itemSource, TView itemsView, BindableProperty itemTemplateProperty, int windowSize = int.MaxValue)
+ {
+ if (itemsView == null)
+ throw new ArgumentNullException("itemsView");
+ if (itemTemplateProperty == null)
+ throw new ArgumentNullException("itemTemplateProperty");
+
+ Parent = parent;
+
+ _itemsView = itemsView;
+ _itemsView.PropertyChanged += BindableOnPropertyChanged;
+ _itemTemplateProperty = itemTemplateProperty;
+
+ if (itemSource != null)
+ {
+ ListProxy = new ListProxy(itemSource, windowSize);
+ ListProxy.CollectionChanged += OnProxyCollectionChanged;
+ }
+ else
+ ListProxy = new ListProxy(new object[0]);
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ event PropertyChangedEventHandler ITemplatedItemsList<TItem>.PropertyChanged
+ {
+ add { PropertyChanged += value; }
+ remove { PropertyChanged -= value; }
+ }
+
+ public event NotifyCollectionChangedEventHandler GroupedCollectionChanged;
+
+ event NotifyCollectionChangedEventHandler ITemplatedItemsList<TItem>.GroupedCollectionChanged
+ {
+ add { GroupedCollectionChanged += value; }
+ remove { GroupedCollectionChanged -= value; }
+ }
+
+ public int Count
+ {
+ get { return ListProxy.Count; }
+ }
+
+ public TItem this[int index]
+ {
+ get { return GetOrCreateContent(index, ListProxy[index]); }
+ }
+
+ object ITemplatedItemsList<TItem>.BindingContext
+ {
+ get { return BindingContext; }
+ }
+
+ IEnumerable ITemplatedItemsList<TItem>.ItemsSource
+ {
+ get { return ListProxy.ProxiedEnumerable; }
+ }
+
+ public TemplatedItemsList<TView, TItem> Parent { get; }
+
+ public BindableProperty ProgressiveLoadingProperty { get; set; }
+
+ public IListProxy ListProxy
+ {
+ get { return (IListProxy)GetValue(ListProxyPropertyKey.BindableProperty); }
+ private set { SetValue(ListProxyPropertyKey, value); }
+ }
+
+ IListProxy ITemplatedItemsList<TItem>.ListProxy
+ {
+ get { return ListProxy; }
+ }
+
+ DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)_itemsView.GetValue(_itemTemplateProperty); }
+ }
+
+ bool ProgressiveLoading
+ {
+ get { return (ProgressiveLoadingProperty != null) && (bool)_itemsView.GetValue(ProgressiveLoadingProperty); }
+ }
+
+ bool ICollection.IsSynchronized
+ {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot
+ {
+ get { return this; }
+ }
+
+ void ICollection.CopyTo(Array array, int arrayIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ bool IList.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ bool IList.IsReadOnly
+ {
+ get { return true; }
+ }
+
+ object IList.this[int index]
+ {
+ get { return this[index]; }
+ set { throw new NotSupportedException(); }
+ }
+
+ int IList.Add(object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ bool IList.Contains(object item)
+ {
+ throw new NotImplementedException();
+ }
+
+ int IList.IndexOf(object item)
+ {
+ return IndexOf((TItem)item);
+ }
+
+ void IList.Insert(int index, object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Remove(object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IDisposable.Dispose()
+ {
+ if (_disposed)
+ return;
+
+ _itemsView.PropertyChanged -= BindableOnPropertyChanged;
+
+ for (var i = 0; i < _templatedObjects.Count; i++)
+ {
+ TItem item = _templatedObjects[i];
+ if (item != null)
+ UnhookItem(item);
+ }
+
+ _disposed = true;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public IEnumerator<TItem> GetEnumerator()
+ {
+ var i = 0;
+ foreach (object item in ListProxy)
+ yield return GetOrCreateContent(i++, item);
+ }
+
+ public int GetDescendantCount()
+ {
+ return Count;
+ }
+
+ public int GetGlobalIndexOfItem(object item)
+ {
+ return ListProxy.IndexOf(item);
+ }
+
+ public int IndexOf(TItem item)
+ {
+ return GetIndex(item);
+ }
+
+ TItem ITemplatedItemsList<TItem>.UpdateContent(TItem content, int index)
+ {
+ return UpdateContent(content, index);
+ }
+
+ internal TItem CreateContent(int index, object item, bool insert = false)
+ {
+ TItem content = ItemTemplate != null ? (TItem)ItemTemplate.CreateContent(item, _itemsView) : _itemsView.CreateDefault(item);
+
+ content = UpdateContent(content, index, item);
+
+ for (int i = _templatedObjects.Count; i <= index; i++)
+ _templatedObjects.Add(null);
+
+ if (!insert)
+ _templatedObjects[index] = content;
+ else
+ _templatedObjects.Insert(index, content);
+
+ return content;
+ }
+
+ int GetIndex(TItem item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ return (int)item.GetValue(IndexProperty);
+ }
+
+ TItem GetOrCreateContent(int index, object item)
+ {
+ TItem content;
+ if (_templatedObjects.Count <= index || (content = _templatedObjects[index]) == null)
+ content = CreateContent(index, item);
+
+ return content;
+ }
+
+ TItem UpdateContent(TItem content, int index, object item)
+ {
+ content.BindingContext = item;
+
+ SetIndex(content, index);
+
+ _itemsView.SetupContent(content, index);
+
+ return content;
+ }
+
+ TItem UpdateContent(TItem content, int index)
+ {
+ object item = ListProxy[index];
+ return UpdateContent(content, index, item);
+ }
+
+ void BindableOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (_itemSourceProperty != null && e.PropertyName == _itemSourceProperty.PropertyName)
+ OnItemsSourceChanged();
+ else if (e.PropertyName == _itemTemplateProperty.PropertyName)
+ OnItemTemplateChanged();
+ else if (ProgressiveLoadingProperty != null && e.PropertyName == ProgressiveLoadingProperty.PropertyName)
+ OnInfiniteScrollingChanged();
+ }
+
+ IList ConvertContent(int startingIndex, IList items, bool forceCreate = false, bool setIndex = false)
+ {
+ var contentItems = new List<TItem>(items.Count);
+ for (var i = 0; i < items.Count; i++)
+ {
+ int index = i + startingIndex;
+ TItem content = !forceCreate ? GetOrCreateContent(index, items[i]) : CreateContent(index, items[i]);
+ if (setIndex)
+ SetIndex(content, index);
+
+ contentItems.Add(content);
+ }
+
+ return contentItems;
+ }
+
+ IEnumerable GetItemsViewSource()
+ {
+ return (IEnumerable)_itemsView.GetValue(_itemSourceProperty);
+ }
+
+ void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler changed = CollectionChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ void OnInfiniteScrollingChanged()
+ {
+ OnItemsSourceChanged();
+ }
+
+ void OnInnerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler handler = GroupedCollectionChanged;
+ if (handler != null)
+ handler(sender, e);
+ }
+
+ void OnItemsSourceChanged(bool fromGrouping = false)
+ {
+ ListProxy.CollectionChanged -= OnProxyCollectionChanged;
+
+ IEnumerable itemSource = GetItemsViewSource();
+ if (itemSource == null)
+ ListProxy = new ListProxy(new object[0]);
+ else
+ ListProxy = new ListProxy(itemSource);
+
+ ListProxy.CollectionChanged += OnProxyCollectionChanged;
+ OnProxyCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ void OnItemTemplateChanged()
+ {
+ if (ListProxy.Count == 0)
+ return;
+
+ OnProxyCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ static void OnListProxyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var til = (TemplatedItemsList<TView, TItem>)bindable;
+ til.OnPropertyChanged("ItemsSource");
+ }
+
+ void OnProxyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ OnProxyCollectionChanged(sender, e, true);
+ }
+
+ void OnProxyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, bool fixWindows = true)
+ {
+ int count = Count;
+ var ex = e as NotifyCollectionChangedEventArgsEx;
+ if (ex != null)
+ count = ex.Count;
+
+ var maxindex = 0;
+ if (e.NewStartingIndex >= 0 && e.NewItems != null)
+ maxindex = Math.Max(maxindex, e.NewStartingIndex + e.NewItems.Count);
+ if (e.OldStartingIndex >= 0 && e.OldItems != null)
+ maxindex = Math.Max(maxindex, e.OldStartingIndex + e.OldItems.Count);
+ if (maxindex > _templatedObjects.Count)
+ _templatedObjects.InsertRange(_templatedObjects.Count, Enumerable.Repeat<TItem>(null, maxindex - _templatedObjects.Count));
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex >= 0)
+ {
+ for (int i = e.NewStartingIndex; i < _templatedObjects.Count; i++)
+ SetIndex(_templatedObjects[i], i + e.NewItems.Count);
+
+ _templatedObjects.InsertRange(e.NewStartingIndex, Enumerable.Repeat<TItem>(null, e.NewItems.Count));
+
+ IList items = ConvertContent(e.NewStartingIndex, e.NewItems, true, true);
+ e = new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Add, items, e.NewStartingIndex);
+ }
+ else
+ {
+ goto case NotifyCollectionChangedAction.Reset;
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Move:
+ if (e.NewStartingIndex < 0 || e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ bool movingForward = e.OldStartingIndex < e.NewStartingIndex;
+
+ if (movingForward)
+ {
+ int moveIndex = e.OldStartingIndex;
+ for (int i = moveIndex + e.OldItems.Count; i <= e.NewStartingIndex; i++)
+ SetIndex(_templatedObjects[i], moveIndex++);
+ }
+ else
+ {
+ for (var i = 0; i < e.OldStartingIndex - e.NewStartingIndex; i++)
+ {
+ TItem item = _templatedObjects[i + e.NewStartingIndex];
+ if (item != null)
+ SetIndex(item, GetIndex(item) + e.OldItems.Count);
+ }
+ }
+
+ TItem[] itemsToMove = _templatedObjects.Skip(e.OldStartingIndex).Take(e.OldItems.Count).ToArray();
+
+ _templatedObjects.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
+ _templatedObjects.InsertRange(e.NewStartingIndex, itemsToMove);
+ for (var i = 0; i < itemsToMove.Length; i++)
+ SetIndex(itemsToMove[i], e.NewStartingIndex + i);
+
+ e = new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Move, itemsToMove, e.NewStartingIndex, e.OldStartingIndex);
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ if (e.OldStartingIndex >= 0)
+ {
+ int removeIndex = e.OldStartingIndex;
+ for (int i = removeIndex + e.OldItems.Count; i < _templatedObjects.Count; i++)
+ SetIndex(_templatedObjects[i], removeIndex++);
+
+ var items = new TItem[e.OldItems.Count];
+ for (var i = 0; i < items.Length; i++)
+ {
+ TItem item = _templatedObjects[e.OldStartingIndex + i];
+ if (item == null)
+ continue;
+
+ UnhookItem(item);
+ items[i] = item;
+ }
+
+ _templatedObjects.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
+ e = new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Remove, items, e.OldStartingIndex);
+ }
+ else
+ {
+ goto case NotifyCollectionChangedAction.Reset;
+ }
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ if (e.NewStartingIndex >= 0)
+ {
+ IList oldItems = ConvertContent(e.NewStartingIndex, e.OldItems);
+ IList newItems = ConvertContent(e.NewStartingIndex, e.NewItems, true, true);
+
+ for (var i = 0; i < oldItems.Count; i++)
+ {
+ UnhookItem((TItem)oldItems[i]);
+ }
+
+ e = new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Replace, newItems, oldItems, e.NewStartingIndex);
+ }
+ else
+ {
+ goto case NotifyCollectionChangedAction.Reset;
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ e = new NotifyCollectionChangedEventArgsEx(count, NotifyCollectionChangedAction.Reset);
+ UnhookAndClear();
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ OnCollectionChanged(e);
+ }
+
+ static void SetIndex(TItem item, int index)
+ {
+ if (item == null)
+ return;
+
+ item.SetValue(IndexProperty, index);
+ }
+
+ void SplitCollectionChangedItems(NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex < 0)
+ goto default;
+
+ for (var i = 0; i < e.NewItems.Count; i++)
+ OnProxyCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.NewItems[i], e.NewStartingIndex + i), false);
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ if (e.OldStartingIndex < 0)
+ goto default;
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ OnProxyCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, e.OldItems[i], e.OldStartingIndex + i), false);
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ if (e.OldStartingIndex < 0)
+ goto default;
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ OnProxyCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, e.NewItems[i], e.OldItems[i], e.OldStartingIndex + i), false);
+
+ break;
+
+ default:
+ OnProxyCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset), false);
+ break;
+ }
+ }
+
+ void UnhookAndClear()
+ {
+ for (var i = 0; i < _templatedObjects.Count; i++)
+ {
+ TItem item = _templatedObjects[i];
+ if (item == null)
+ continue;
+
+ UnhookItem(item);
+ }
+
+ _templatedObjects.Clear();
+ }
+
+ void UnhookItem(TItem item)
+ {
+ SetIndex(item, -1);
+ _itemsView.UnhookContent(item);
+
+ item.BindingContext = null;
+ }
+
+ // Below properties and methods are NOT used in GridView.
+ // For porting ItemsView from Xamarin.Forms, implementing ITemplatedItemsList(Xamarin.Forms) is needed for type checking,
+ // but these are unnecessary in GridView.
+ #region ITemplatedItemsList
+
+ IReadOnlyList<string> ITemplatedItemsList<TItem>.ShortNames
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ string ITemplatedItemsList<TItem>.Name
+ {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ TItem ITemplatedItemsList<TItem>.HeaderContent
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ int ITemplatedItemsList<TItem>.GetGlobalIndexForGroup(ITemplatedItemsList<TItem> group)
+ {
+ throw new NotImplementedException();
+ }
+
+ ITemplatedItemsList<TItem> ITemplatedItemsList<TItem>.GetGroup(int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ Tuple<int, int> ITemplatedItemsList<TItem>.GetGroupAndIndexOfItem(object item)
+ {
+ throw new NotImplementedException();
+ }
+
+ Tuple<int, int> ITemplatedItemsList<TItem>.GetGroupAndIndexOfItem(object group, object item)
+ {
+ throw new NotImplementedException();
+ }
+
+ int ITemplatedItemsList<TItem>.GetGroupIndexFromGlobal(int globalIndex, out int leftOver)
+ {
+ throw new NotImplementedException();
+ }
+
+ TItem ITemplatedItemsList<TItem>.UpdateHeader(TItem content, int groupIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
<ItemGroup>\r
<Compile Include="Background.cs" />\r
<Compile Include="BackgroundOptions.cs" />\r
+ <Compile Include="Cells\GridViewCell.cs" />\r
<Compile Include="Cells\Type1Cell.cs" />\r
<Compile Include="Cells\DoubleLabelCell.cs" />\r
<Compile Include="Cells\Type2Cell.cs" />\r
<Compile Include="Cells\BaseTypeCell.cs" />\r
<Compile Include="Cells\MultilineCell.cs" />\r
+ <Compile Include="EnumerableExtensions.cs" />\r
+ <Compile Include="GridView.cs" />\r
+ <Compile Include="GridViewEnums.cs" />\r
+ <Compile Include="GridViewEventArgs.cs" />\r
+ <Compile Include="ItemsView.cs" />\r
+ <Compile Include="ListProxy.cs" />\r
<Compile Include="Properties\AssemblyInfo.cs" />\r
<Compile Include="RadioButton.cs" />\r
<Compile Include="SelectedEventArgs.cs" />\r
+ <Compile Include="ReadOnlyListAdapter.cs" />\r
+ <Compile Include="TemplatedItemsList.cs" />\r
</ItemGroup>\r
<ItemGroup>\r
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">\r
<Target Name="AfterBuild">
</Target>
-->\r
-</Project>
\ No newline at end of file
+</Project>