diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.WinRT/CellControl.cs | |
download | xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2 xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip |
Initial import
Diffstat (limited to 'Xamarin.Forms.Platform.WinRT/CellControl.cs')
-rw-r--r-- | Xamarin.Forms.Platform.WinRT/CellControl.cs | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.WinRT/CellControl.cs b/Xamarin.Forms.Platform.WinRT/CellControl.cs new file mode 100644 index 00000000..a47c9646 --- /dev/null +++ b/Xamarin.Forms.Platform.WinRT/CellControl.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using Windows.UI.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; + +#if WINDOWS_UWP + +namespace Xamarin.Forms.Platform.UWP +#else + +namespace Xamarin.Forms.Platform.WinRT +#endif +{ + public class CellControl : ContentControl + { + public static readonly DependencyProperty CellProperty = DependencyProperty.Register("Cell", typeof(object), typeof(CellControl), + new PropertyMetadata(null, (o, e) => ((CellControl)o).SetSource((Cell)e.OldValue, (Cell)e.NewValue))); + + public static readonly DependencyProperty IsGroupHeaderProperty = DependencyProperty.Register("IsGroupHeader", typeof(bool), typeof(CellControl), null); + + internal static readonly BindableProperty MeasuredEstimateProperty = BindableProperty.Create("MeasuredEstimate", typeof(double), typeof(ListView), -1d); + readonly Lazy<ListView> _listView; + readonly PropertyChangedEventHandler _propertyChangedHandler; + + IList<MenuItem> _contextActions; + Windows.UI.Xaml.DataTemplate _currentTemplate; + bool _isListViewRealized; + object _newValue; + + public CellControl() + { + _listView = new Lazy<ListView>(GetListView); + + DataContextChanged += OnDataContextChanged; + + Unloaded += (sender, args) => + { + Cell cell = Cell; + if (cell != null) + cell.SendDisappearing(); + }; + + _propertyChangedHandler = OnCellPropertyChanged; + } + + public Cell Cell + { + get { return (Cell)GetValue(CellProperty); } + set { SetValue(CellProperty, value); } + } + + public bool IsGroupHeader + { + get { return (bool)GetValue(IsGroupHeaderProperty); } + set { SetValue(IsGroupHeaderProperty, value); } + } + + protected FrameworkElement CellContent + { + get { return (FrameworkElement)Content; } + } + + protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize) + { + ListView lv = _listView.Value; + + // set the Cell now that we have a reference to the ListView, since it will have been skipped + // on DataContextChanged. + if (_newValue != null) + { + SetCell(_newValue); + _newValue = null; + } + + if (Content == null) + { + if (lv != null) + { + if (lv.HasUnevenRows) + { + var estimate = (double)lv.GetValue(MeasuredEstimateProperty); + if (estimate > -1) + return new Windows.Foundation.Size(availableSize.Width, estimate); + } + else + { + double rowHeight = lv.RowHeight; + if (rowHeight > -1) + return new Windows.Foundation.Size(availableSize.Width, rowHeight); + } + } + + return new Windows.Foundation.Size(0, 0); + } + + // Children still need measure called on them + Windows.Foundation.Size result = base.MeasureOverride(availableSize); + + if (lv != null) + { + lv.SetValue(MeasuredEstimateProperty, result.Height); + } + + return result; + } + + static string GetDisplayTextFromGroup(ListView lv, TemplatedItemsList<ItemsView<Cell>, Cell> group) + { + string displayBinding = null; + + if (lv.GroupDisplayBinding != null) + displayBinding = group.Name; + + if (lv.GroupShortNameBinding != null) + displayBinding = group.ShortName; + + // TODO: what if they set both? should it default to the ShortName, like it will here? + // ShortNames binding did not appear to be functional before. + return displayBinding; + } + + static TemplatedItemsList<ItemsView<Cell>, Cell> GetGroup(object newContext, ListView lv) + { + int groupIndex = lv.TemplatedItems.GetGlobalIndexOfGroup(newContext); + TemplatedItemsList<ItemsView<Cell>, Cell> group = lv.TemplatedItems.GetGroup(groupIndex); + return group; + } + + ListView GetListView() + { + DependencyObject parent = VisualTreeHelper.GetParent(this); + while (parent != null) + { + var lv = parent as ListViewRenderer; + if (lv != null) + { + _isListViewRealized = true; + return lv.Element; + } + + parent = VisualTreeHelper.GetParent(parent); + } + + return null; + } + + Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell) + { + var renderer = Registrar.Registered.GetHandler<ICellRenderer>(cell.GetType()); + return renderer.GetTemplate(cell); + } + + void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "HasContextActions") + { + SetupContextMenu(); + } + } + + void OnClick(object sender, PointerRoutedEventArgs e) + { + PointerPoint point = e.GetCurrentPoint(CellContent); + if (point.Properties.PointerUpdateKind != PointerUpdateKind.RightButtonReleased) + return; + + OpenContextMenu(); + } + + void OnContextActionsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + var flyout = FlyoutBase.GetAttachedFlyout(CellContent) as MenuFlyout; + if (flyout != null) + { + flyout.Items.Clear(); + SetupMenuItems(flyout); + } + } + + void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) + { + // We don't want to set the Cell until the ListView is realized, just in case the + // Cell has an ItemTemplate. Instead, we'll store the new data item, and it will be + // set on MeasureOverrideDelegate. However, if the parent is a TableView, we'll already + // have a complete Cell object to work with, so we can move ahead. + if (_isListViewRealized || args.NewValue is Cell) + SetCell(args.NewValue); + else if (args.NewValue != null) + _newValue = args.NewValue; + } + + void OnLongTap(object sender, HoldingRoutedEventArgs e) + { + if (e.HoldingState == HoldingState.Started) + OpenContextMenu(); + } + + void OnOpenContext(object sender, RightTappedRoutedEventArgs e) + { + FlyoutBase.ShowAttachedFlyout(CellContent); + } + + void OpenContextMenu() + { + if (FlyoutBase.GetAttachedFlyout(CellContent) == null) + { + var flyout = new MenuFlyout(); + SetupMenuItems(flyout); + + ((INotifyCollectionChanged)Cell.ContextActions).CollectionChanged += OnContextActionsChanged; + + _contextActions = Cell.ContextActions; + FlyoutBase.SetAttachedFlyout(CellContent, flyout); + } + + FlyoutBase.ShowAttachedFlyout(CellContent); + } + + void SetCell(object newContext) + { + var cell = newContext as Cell; + + if (ReferenceEquals(Cell?.BindingContext, newContext)) + return; + + // If there is a ListView, load the Cell content from the ItemTemplate. + // Otherwise, the given Cell is already a templated Cell from a TableView. + ListView lv = _listView.Value; + if (lv != null) + { + bool isGroupHeader = IsGroupHeader; + DataTemplate template = isGroupHeader ? lv.GroupHeaderTemplate : lv.ItemTemplate; + + if (template is DataTemplateSelector) + { + template = ((DataTemplateSelector)template).SelectTemplate(newContext, lv); + } + + if (template != null) + { + cell = template.CreateContent() as Cell; + cell.BindingContext = newContext; + } + else + { + string textContent = newContext.ToString(); + + if (isGroupHeader) + { + TemplatedItemsList<ItemsView<Cell>, Cell> group = GetGroup(newContext, lv); + textContent = GetDisplayTextFromGroup(lv, group); + } + + cell = lv.CreateDefaultCell(textContent); + } + + // A TableView cell should already have its parent, + // but we need to set the parent for a ListView cell. + cell.Parent = lv; + + // This provides the Group Header styling (e.g., larger font, etc.) when the + // template is loaded later. + TemplatedItemsList<ItemsView<Cell>, Cell>.SetIsGroupHeader(cell, isGroupHeader); + } + + Cell = cell; + } + + void SetSource(Cell oldCell, Cell newCell) + { + if (oldCell != null) + { + oldCell.PropertyChanged -= _propertyChangedHandler; + oldCell.SendDisappearing(); + } + + if (newCell != null) + { + newCell.SendAppearing(); + + UpdateContent(newCell); + SetupContextMenu(); + + newCell.PropertyChanged += _propertyChangedHandler; + } + } + + void SetupContextMenu() + { + if (CellContent == null || Cell == null) + return; + + if (!Cell.HasContextActions) + { + CellContent.Holding -= OnLongTap; + CellContent.PointerReleased -= OnClick; + if (_contextActions != null) + { + ((INotifyCollectionChanged)_contextActions).CollectionChanged -= OnContextActionsChanged; + _contextActions = null; + } + + FlyoutBase.SetAttachedFlyout(CellContent, null); + return; + } + + CellContent.PointerReleased += OnClick; + CellContent.Holding += OnLongTap; + } + + void SetupMenuItems(MenuFlyout flyout) + { + foreach (MenuItem item in Cell.ContextActions) + { + var flyoutItem = new MenuFlyoutItem(); + flyoutItem.SetBinding(MenuFlyoutItem.TextProperty, "Text"); + flyoutItem.Command = new MenuItemCommand(item); + flyoutItem.DataContext = item; + + flyout.Items.Add(flyoutItem); + } + } + + void UpdateContent(Cell newCell) + { + Windows.UI.Xaml.DataTemplate dt = GetTemplate(newCell); + if (dt != _currentTemplate || Content == null) + { + _currentTemplate = dt; + Content = dt.LoadContent(); + } + + ((FrameworkElement)Content).DataContext = newCell; + } + } +}
\ No newline at end of file |