зеркало из https://github.com/DeGsoft/maui-linux.git
439 строки
14 KiB
C#
439 строки
14 KiB
C#
using System;
|
|
using System.Collections.Specialized;
|
|
using System.Maui.Internals;
|
|
using ElmSharp;
|
|
|
|
namespace System.Maui.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
|
|
/// System.Maui.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>
|
|
{
|
|
IListViewController Controller => Element;
|
|
ITemplatedItemsView<Cell> TemplatedItemsView => Element;
|
|
|
|
/// <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="System.Maui.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()
|
|
{
|
|
RegisterPropertyHandler(ListView.IsGroupingEnabledProperty, UpdateIsGroupingEnabled);
|
|
RegisterPropertyHandler(ListView.HasUnevenRowsProperty, UpdateHasUnevenRows);
|
|
RegisterPropertyHandler(ListView.RowHeightProperty, UpdateRowHeight);
|
|
RegisterPropertyHandler(ListView.SelectedItemProperty, UpdateSelectedItem);
|
|
RegisterPropertyHandler(ListView.ItemsSourceProperty, UpdateSource);
|
|
RegisterPropertyHandler("HeaderElement", UpdateHeader);
|
|
RegisterPropertyHandler("FooterElement", UpdateFooter);
|
|
RegisterPropertyHandler(ListView.SelectionModeProperty, UpdateSelectionMode);
|
|
RegisterPropertyHandler(ListView.VerticalScrollBarVisibilityProperty, UpdateVerticalScrollBarVisibility);
|
|
RegisterPropertyHandler(ListView.HorizontalScrollBarVisibilityProperty, UpdateHorizontalScrollBarVisibility);
|
|
}
|
|
|
|
/// <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="System.Maui.Platform.Tizen.ElementChangedEventArgs"/>.</param>
|
|
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
|
|
{
|
|
if (Control == null)
|
|
{
|
|
SetNativeControl(CreateNativeControl());
|
|
|
|
Control.Scrolled += OnScrolled;
|
|
Control.ItemSelected += OnListViewItemSelected;
|
|
Control.ItemUnselected += OnListViewItemUnselected;
|
|
}
|
|
|
|
if (e.OldElement != null)
|
|
{
|
|
e.OldElement.ScrollToRequested -= OnScrollToRequested;
|
|
e.OldElement.TemplatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged;
|
|
e.OldElement.TemplatedItems.CollectionChanged -= OnCollectionChanged;
|
|
}
|
|
|
|
if (e.NewElement != null)
|
|
{
|
|
Element.ScrollToRequested += OnScrollToRequested;
|
|
Element.TemplatedItems.GroupedCollectionChanged += OnGroupedCollectionChanged;
|
|
Element.TemplatedItems.CollectionChanged += OnCollectionChanged;
|
|
}
|
|
|
|
base.OnElementChanged(e);
|
|
}
|
|
|
|
protected virtual Native.ListView CreateNativeControl()
|
|
{
|
|
if(Device.Idiom == TargetIdiom.Watch)
|
|
{
|
|
return new Native.Watch.WatchListView(System.Maui.Maui.NativeParent, System.Maui.Maui.CircleSurface);
|
|
}
|
|
else
|
|
{
|
|
return new Native.ListView(System.Maui.Maui.NativeParent);
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (Element != null)
|
|
{
|
|
Element.ScrollToRequested -= OnScrollToRequested;
|
|
Element.TemplatedItems.CollectionChanged -= OnCollectionChanged;
|
|
Element.TemplatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged;
|
|
}
|
|
|
|
if (Control != null)
|
|
{
|
|
Control.Scrolled -= OnScrolled;
|
|
Control.ItemSelected -= OnListViewItemSelected;
|
|
Control.ItemUnselected -= OnListViewItemUnselected;
|
|
}
|
|
}
|
|
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>
|
|
protected void OnListViewItemSelected(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>
|
|
protected void OnListViewItemUnselected(object sender, GenListItemEventArgs e)
|
|
{
|
|
if (_selectedItemChanging == 0)
|
|
{
|
|
_lastSelectedItem = null;
|
|
}
|
|
}
|
|
|
|
void OnScrolled(object sender, EventArgs e)
|
|
{
|
|
var region = Control.CurrentRegion.ToDP();
|
|
Element.SendScrolled(new ScrolledEventArgs(region.X, region.Y));
|
|
}
|
|
|
|
/// <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 = TemplatedItemsView.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()
|
|
{
|
|
bool hasHeader = Control.HasHeader();
|
|
bool hasFooter = Control.HasFooter();
|
|
|
|
Control.Clear();
|
|
Control.AddSource(Element.TemplatedItems);
|
|
UpdateSelectedItem();
|
|
|
|
if (hasHeader)
|
|
UpdateHeader();
|
|
if (hasFooter)
|
|
UpdateFooter();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the header.
|
|
/// </summary>
|
|
void UpdateHeader()
|
|
{
|
|
Control.SetHeader(((IListViewController)Element).HeaderElement as VisualElement);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the footer.
|
|
/// </summary>
|
|
void UpdateFooter()
|
|
{
|
|
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(bool initialize)
|
|
{
|
|
if (initialize)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method is used for programaticaly selecting choosen item.
|
|
/// </summary>
|
|
void UpdateSelectedItem()
|
|
{
|
|
if (Element.SelectedItem == null)
|
|
{
|
|
if (_lastSelectedItem != null)
|
|
{
|
|
_lastSelectedItem.IsSelected = false;
|
|
_lastSelectedItem = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var templatedItems = TemplatedItemsView.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateSelectionMode()
|
|
{
|
|
if (Element.SelectionMode == ListViewSelectionMode.None)
|
|
{
|
|
Element.SelectedItem = null;
|
|
Control.IsHighlight = false;
|
|
}
|
|
else
|
|
{
|
|
Control.IsHighlight = true;
|
|
}
|
|
}
|
|
|
|
void UpdateVerticalScrollBarVisibility()
|
|
{
|
|
Control.VerticalScrollBarVisibility = Element.VerticalScrollBarVisibility.ToNative();
|
|
}
|
|
|
|
void UpdateHorizontalScrollBarVisibility()
|
|
{
|
|
Control.HorizontalScrollBarVisibility = Element.HorizontalScrollBarVisibility.ToNative();
|
|
}
|
|
}
|
|
}
|