summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
diff options
context:
space:
mode:
authorKangho Hur <kangho.hur@samsung.com>2016-12-16 11:00:07 +0900
committerKangho Hur <kangho.hur@samsung.com>2017-10-23 13:34:24 +0900
commita2e67107402bc5a49d73cee9062bcd7dbe4069e7 (patch)
treeea6c6606567e8440397de192d47195194c0266dd /Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
parent509954d0a013acdf7508644c1fb394bea626e587 (diff)
downloadxamarin-forms-a2e67107402bc5a49d73cee9062bcd7dbe4069e7.tar.gz
xamarin-forms-a2e67107402bc5a49d73cee9062bcd7dbe4069e7.tar.bz2
xamarin-forms-a2e67107402bc5a49d73cee9062bcd7dbe4069e7.zip
Add Tizen backend renderer
- Xamarin.Forms.Platform.Tizen has been added - Xamarin.Forms.Maps.Tizen has been added - RPM build spec has been added Change-Id: I0021e0f040d97345affc87512ee0f6ce437f4e6d
Diffstat (limited to 'Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs437
1 files changed, 437 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
new file mode 100644
index 00000000..5179d7e0
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
@@ -0,0 +1,437 @@
+using System;
+using System.Collections.Specialized;
+using ElmSharp;
+using EProgressBar = ElmSharp.ProgressBar;
+using ERect = ElmSharp.Rect;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Renderer class for Xamarin ListView class. This provides necessary logic translating
+ /// Xamarin API to Tizen Native API. This is a derivate of a ViewRenderer base class.
+ /// This is a template class with two template parameters. First one is restricted to
+ /// Xamarin.Forms.View and can be accessed via property Element. This represent actual
+ /// xamarin view which represents control logic. Second one is restricted to ElmSharp.Widget
+ /// types, and can be accessed with Control property. This represents actual native control
+ /// which is used to draw control and realize xamarin forms api.
+ /// </summary>
+ public class ListViewRenderer : ViewRenderer<ListView, Native.ListView>, IDisposable
+ {
+ /// <summary>
+ /// Event handler for ScrollToRequested.
+ /// </summary>
+ readonly EventHandler<ScrollToRequestedEventArgs> _scrollToRequested;
+
+ /// <summary>
+ /// Event handler for collection changed.
+ /// </summary>
+ readonly NotifyCollectionChangedEventHandler _collectionChanged;
+
+ /// <summary>
+ /// Event handler for grouped collection changed.
+ /// </summary>
+ readonly NotifyCollectionChangedEventHandler _groupedCollectionChanged;
+
+ /// <summary>
+ /// The _lastSelectedItem and _selectedItemChanging are used for realizing ItemTapped event. Since Xamarin
+ /// needs information only when an item has been taped, native handlers need to be agreagated
+ /// and NotifyRowTapped has to be realized with this.
+ /// </summary>
+
+ GenListItem _lastSelectedItem = null;
+ int _selectedItemChanging = 0;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.ListViewRenderer"/> class.
+ /// Note that at this stage of construction renderer dose not have required native element. This should
+ /// only be used with xamarin engine.
+ /// </summary>
+ public ListViewRenderer()
+ {
+ _scrollToRequested = OnScrollToRequested;
+ _collectionChanged = OnCollectionChanged;
+ _groupedCollectionChanged = OnGroupedCollectionChanged;
+
+ RegisterPropertyHandler(ListView.IsGroupingEnabledProperty, UpdateIsGroupingEnabled);
+ RegisterPropertyHandler(ListView.HasUnevenRowsProperty, UpdateHasUnevenRows);
+ RegisterPropertyHandler(ListView.RowHeightProperty, UpdateRowHeight);
+ RegisterPropertyHandler(ListView.HeaderProperty, UpdateHeader);
+ RegisterPropertyHandler(ListView.SelectedItemProperty, UpdateSelectedItem);
+ RegisterPropertyHandler(ListView.FooterProperty, UpdateFooter);
+ RegisterPropertyHandler(ListView.ItemsSourceProperty, UpdateSource);
+ RegisterPropertyHandler(ListView.FooterTemplateProperty, UpdateFooter);
+ RegisterPropertyHandler(ListView.HeaderTemplateProperty, UpdateHeader);
+ }
+
+ /// <summary>
+ /// Invoked on creation of new ListView renderer. Handles the creation of a native
+ /// element and initialization of the renderer.
+ /// </summary>
+ /// <param name="e"><see cref="Xamarin.Forms.Platform.Tizen.ElementChangedEventArgs"/>.</param>
+ protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Native.ListView(Forms.Context.MainWindow));
+ }
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.ScrollToRequested -= _scrollToRequested;
+ if (Element.IsGroupingEnabled)
+ {
+ e.OldElement.TemplatedItems.GroupedCollectionChanged -= _groupedCollectionChanged;
+ }
+ e.OldElement.TemplatedItems.CollectionChanged -= _collectionChanged;
+ Control.ItemSelected -= ListViewItemSelectedHandler;
+ Control.ItemUnselected -= ListViewItemUnselectedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.ScrollToRequested += _scrollToRequested;
+ Element.TemplatedItems.CollectionChanged += _collectionChanged;
+ Control.ItemSelected += ListViewItemSelectedHandler;
+ Control.ItemUnselected += ListViewItemUnselectedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ /// <summary>
+ /// Handles the disposing of an existing renderer instance. Results in event handlers
+ /// being detached and a Dispose() method from base class (VisualElementRenderer) being invoked.
+ /// </summary>
+ /// <param name="disposing">A boolean flag passed to the invocation of base class' Dispose() method.
+ /// <c>True</c> if the memory release was requested on demand.</param>
+ protected override void Dispose(bool disposing)
+ {
+ Element.ScrollToRequested -= _scrollToRequested;
+ Element.TemplatedItems.CollectionChanged -= _collectionChanged;
+ Element.TemplatedItems.GroupedCollectionChanged -= _groupedCollectionChanged;
+
+ base.Dispose(disposing);
+ }
+
+ /// <summary>
+ /// Handles item selected event. Note that it has to handle selection also for grouping mode as well.
+ /// As a result of this method, ItemTapped event should be invoked in Xamarin.
+ /// </summary>
+ /// <param name="sender">A native list instance from which the event has originated.</param>
+ /// <param name="e">Argument associated with handler, it holds native item for which event was raised</param>
+ void ListViewItemSelectedHandler(object sender, GenListItemEventArgs e)
+ {
+ GenListItem item = e.Item;
+
+ _lastSelectedItem = item;
+
+ if (_selectedItemChanging == 0)
+ {
+ if (item != null)
+ {
+ int index = -1;
+ if (Element.IsGroupingEnabled)
+ {
+ Native.ListView.ItemContext itemContext = item.Data as Native.ListView.ItemContext;
+ if (itemContext.IsGroupItem)
+ {
+ return;
+ }
+ else
+ {
+ int groupIndex = (Element.TemplatedItems as System.Collections.IList).IndexOf(itemContext.ListOfSubItems);
+ int inGroupIndex = itemContext.ListOfSubItems.IndexOf(itemContext.Cell);
+
+ ++_selectedItemChanging;
+ Element.NotifyRowTapped(groupIndex, inGroupIndex);
+ --_selectedItemChanging;
+ }
+ }
+ else
+ {
+ index = Element.TemplatedItems.IndexOf((item.Data as Native.ListView.ItemContext).Cell);
+
+ ++_selectedItemChanging;
+ Element.NotifyRowTapped(index);
+ --_selectedItemChanging;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Handles item unselected event.
+ /// </summary>
+ /// <param name="sender">A native list instance from which the event has originated.</param>
+ /// <param name="e">Argument associated with handler, it holds native item for which event was raised</param>
+ void ListViewItemUnselectedHandler(object sender, GenListItemEventArgs e)
+ {
+ if (_selectedItemChanging == 0)
+ {
+ _lastSelectedItem = null;
+ }
+ }
+
+ /// <summary>
+ /// This is method handles "scroll to" requests from xamarin events.
+ /// It allows for scrolling to specified item on list view.
+ /// </summary>
+ /// <param name="sender">A native list instance from which the event has originated.</param>
+ /// <param name="e">ScrollToRequestedEventArgs.</param>
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ Cell cell;
+ int position;
+ var scrollArgs = (ITemplatedItemsListScrollToRequestedEventArgs)e;
+
+ var templatedItems = Element.TemplatedItems;
+ if (Element.IsGroupingEnabled)
+ {
+ var results = templatedItems.GetGroupAndIndexOfItem(scrollArgs.Group, scrollArgs.Item);
+ if (results.Item1 == -1 || results.Item2 == -1)
+ return;
+
+ var group = templatedItems.GetGroup(results.Item1);
+ cell = group[results.Item2];
+ }
+ else
+ {
+ position = templatedItems.GetGlobalIndexOfItem(scrollArgs.Item);
+ cell = templatedItems[position];
+ }
+
+ Control.ApplyScrollTo(cell, e.Position, e.ShouldAnimate);
+ }
+
+ /// <summary>
+ /// Helper class for managing proper postion of Header and Footer element.
+ /// Since both elements need to be implemented with ordinary list elements,
+ /// both header and footer are removed at first, then the list is being modified
+ /// and finally header and footer are prepended and appended to the list, respectively.
+ /// </summary>
+ class HeaderAndFooterHandler : IDisposable
+ {
+ VisualElement headerElement;
+ VisualElement footerElement;
+
+ Native.ListView Control;
+
+ public HeaderAndFooterHandler(Widget control)
+ {
+ Control = control as Native.ListView;
+
+ if (Control.HasHeader())
+ {
+ headerElement = Control.GetHeader();
+ Control.RemoveHeader();
+ }
+ if (Control.HasFooter())
+ {
+ footerElement = Control.GetFooter();
+ Control.RemoveFooter();
+ }
+ }
+
+ public void Dispose()
+ {
+ if (headerElement != null)
+ {
+ Control.SetHeader(headerElement);
+ }
+ if (footerElement != null)
+ {
+ Control.SetFooter(footerElement);
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method is called whenever something changes in list view data model.
+ /// Method will not be invoked for grouping mode, but for example event with
+ /// action reset will be handled here when switching between group and no-group mode.
+ /// </summary>
+ /// <param name="sender">TemplatedItemsList<ItemsView<Cell>, Cell>.</param>
+ /// <param name="e">NotifyCollectionChangedEventArgs.</param>
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ using (new HeaderAndFooterHandler(Control))
+ {
+ 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];
+ }
+ Control.AddSource(e.NewItems, before);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ Control.Remove(e.OldItems);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ UpdateSource();
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method is called whenever something changes in list view data model.
+ /// Method will be invoked for grouping mode, but some action can be also handled
+ /// by OnCollectionChanged handler.
+ /// </summary>
+ /// <param name="sender">TemplatedItemsList<ItemsView<Cell>, Cell>.</param>
+ /// <param name="e">NotifyCollectionChangedEventArgs.</param>
+ void OnGroupedCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ using (new HeaderAndFooterHandler(Control))
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ TemplatedItemsList<ItemsView<Cell>,Cell> itemsGroup = sender as TemplatedItemsList<ItemsView<Cell>,Cell>;
+ Cell before = null;
+ if (e.NewStartingIndex + e.NewItems.Count < itemsGroup.Count)
+ {
+ before = itemsGroup[e.NewStartingIndex + e.NewItems.Count];
+ }
+ Control.AddItemsToGroup(itemsGroup, e.NewItems, before);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ Control.Remove(e.OldItems);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ Control.ResetGroup(sender as TemplatedItemsList<ItemsView<Cell>, Cell>);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the source.
+ /// </summary>
+ void UpdateSource()
+ {
+ Control.Clear();
+ Control.AddSource(Element.TemplatedItems);
+ }
+
+ /// <summary>
+ /// Updates the header.
+ /// </summary>
+ void UpdateHeader()
+ {
+ if (Element.Header == null)
+ {
+ Control.SetHeader(null);
+ return;
+ }
+
+ if (((IListViewController)Element).HeaderElement == null)
+ {
+ Device.StartTimer(new TimeSpan(0), () =>
+ {
+ Control.SetHeader(((IListViewController)Element).HeaderElement as VisualElement);
+ return false;
+ });
+ }
+ else
+ {
+ Control.SetHeader(((IListViewController)Element).HeaderElement as VisualElement);
+ }
+ }
+
+ /// <summary>
+ /// Updates the footer.
+ /// </summary>
+ void UpdateFooter()
+ {
+ if (Element.Footer == null)
+ {
+ Control.SetFooter(null);
+ return;
+ }
+
+
+ if (((IListViewController)Element).FooterElement == null)
+ {
+ Device.StartTimer(new TimeSpan(0), () =>
+ {
+ Control.SetFooter(((IListViewController)Element).FooterElement as VisualElement);
+ return false;
+ });
+ }
+ else
+ {
+ Control.SetFooter(((IListViewController)Element).FooterElement as VisualElement);
+ }
+ }
+
+ /// <summary>
+ /// Updates the has uneven rows.
+ /// </summary>
+ void UpdateHasUnevenRows()
+ {
+ Control.SetHasUnevenRows(Element.HasUnevenRows);
+ }
+
+ /// <summary>
+ /// Updates the height of the row.
+ /// </summary>
+ void UpdateRowHeight()
+ {
+ Control.UpdateRealizedItems();
+ }
+
+ /// <summary>
+ /// Updates the is grouping enabled.
+ /// </summary>
+ /// <param name="initialize">If set to <c>true</c>, this method is invoked during initialization
+ /// (otherwise it will be invoked only after property changes).</param>
+ void UpdateIsGroupingEnabled(bool initialize)
+ {
+ Control.IsGroupingEnabled = Element.IsGroupingEnabled;
+ if (Element.IsGroupingEnabled)
+ {
+ Element.TemplatedItems.GroupedCollectionChanged += _groupedCollectionChanged;
+ }
+ else
+ {
+ Element.TemplatedItems.GroupedCollectionChanged -= _groupedCollectionChanged;
+ }
+ }
+
+ /// <summary>
+ /// Method is used for programaticaly selecting choosen item.
+ /// </summary>
+ void UpdateSelectedItem()
+ {
+ if (_selectedItemChanging == 0)
+ {
+ if (Element.SelectedItem == null)
+ {
+ if (_lastSelectedItem != null)
+ {
+ _lastSelectedItem.IsSelected = false;
+ _lastSelectedItem = null;
+ }
+ }
+ else
+ {
+ var templatedItems = Element.TemplatedItems;
+ var results = templatedItems.GetGroupAndIndexOfItem(Element.SelectedItem);
+ if (results.Item1 != -1 && results.Item2 != -1)
+ {
+ var itemGroup = templatedItems.GetGroup(results.Item1);
+ var cell = itemGroup[results.Item2];
+
+ ++_selectedItemChanging;
+ Control.ApplySelectedItem(cell);
+ --_selectedItemChanging;
+ }
+ }
+ }
+ }
+ }
+}