diff options
author | Kangho Hur <kangho.hur@samsung.com> | 2016-12-16 11:00:07 +0900 |
---|---|---|
committer | Kangho Hur <kangho.hur@samsung.com> | 2017-10-23 13:34:24 +0900 |
commit | a2e67107402bc5a49d73cee9062bcd7dbe4069e7 (patch) | |
tree | ea6c6606567e8440397de192d47195194c0266dd /Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs | |
parent | 509954d0a013acdf7508644c1fb394bea626e587 (diff) | |
download | xamarin-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.cs | 437 |
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; + } + } + } + } + } +} |