Xamarin Forms UICollectionView with DataTemplate

I am trying to create a custom view in Xamarin Forms that translates to a UICollectionView on iOS.

This is the first thing that is pretty simple to do:

View:

public class CollectionView : View { } 

Renderer:

 public class CollectionViewRenderer : ViewRenderer<CollectionView, UICollectionView> { protected override void OnElementChanged(ElementChangedEventArgs<CollectionView> e) { base.OnElementChanged(e); if (Control == null) { SetNativeControl(new UICollectionView(new CGRect(0, 0, 200, 200), new UICollectionViewFlowLayout())); } if (e.NewElement != null) { ... Control.Source = new CollectionViewSource(a, this); Control.ReloadData(); } } } 

Now I would like to feed this CollectionView using a DataTemplates (from a DataTemplateSelector). But I cannot find a way to register classes:

From the template you can:

 Template.CreateContent(); 

to get the user interface element.

But how can I register it in the View collection for dequeue'ing in the CollectionSource

eg:.

 CollectionView.RegisterClassForCell(typeof(????), "CellId"); 
+5
source share
1 answer

Hope this helps you !!!

Customcontrol

GridCollectionView.cs
 using System; using CoreGraphics; using Foundation; using UIKit; namespace MyApp.Forms.Controls { public class GridCollectionView : UICollectionView { public GridCollectionView () : this (default(CGRect)) { } public GridCollectionView(CGRect frm) : base(frm, new UICollectionViewFlowLayout()) { AutoresizingMask = UIViewAutoresizing.All; ContentMode = UIViewContentMode.ScaleToFill; RegisterClassForCell(typeof(GridViewCell), new NSString (GridViewCell.Key)); } public bool SelectionEnable { get; set; } public double RowSpacing { get { return ((UICollectionViewFlowLayout)this.CollectionViewLayout).MinimumLineSpacing; } set { ((UICollectionViewFlowLayout)this.CollectionViewLayout).MinimumLineSpacing = (nfloat)value; } } public double ColumnSpacing { get { return ((UICollectionViewFlowLayout)this.CollectionViewLayout).MinimumInteritemSpacing; } set { ((UICollectionViewFlowLayout)this.CollectionViewLayout).MinimumInteritemSpacing = (nfloat)value; } } public CGSize ItemSize { get { return ((UICollectionViewFlowLayout)this.CollectionViewLayout).ItemSize; } set { ((UICollectionViewFlowLayout)this.CollectionViewLayout).ItemSize = value; } } public override UICollectionViewCell CellForItem(NSIndexPath indexPath) { if (indexPath == null) { //calling base.CellForItem(indexPath) when indexPath is null causes an exception. //indexPath could be null in the following scenario: // - GridView is configured to show 2 cells per row and there are 3 items in ItemsSource collection // - you're trying to drag 4th cell (empty) like you're trying to scroll return null; } return base.CellForItem(indexPath); } public override void Draw (CGRect rect) { this.CollectionViewLayout.InvalidateLayout (); base.Draw (rect); } public override CGSize SizeThatFits(CGSize size) { return ItemSize; } } } 

Renderer class

Gridviewrenderer.cs
 using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using Foundation; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; using MyApp.Controls; using System.Collections.Generic; [assembly: ExportRenderer (typeof(GridView), typeof(GridViewRenderer))] namespace MyApp.Controls { public class GridViewRenderer: ViewRenderer<GridView,GridCollectionView> { private GridDataSource _dataSource; public GridViewRenderer () { } public int RowsInSection(UICollectionView collectionView, nint section) { return ((ICollection) this.Element.ItemsSource).Count; } public void ItemSelected(UICollectionView tableView, NSIndexPath indexPath) { var item = this.Element.ItemsSource.Cast<object>().ElementAt(indexPath.Row); this.Element.InvokeItemSelectedEvent(this, item); } public UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) { var item = this.Element.ItemsSource.Cast<object>().ElementAt(indexPath.Row); var viewCellBinded = (this.Element.ItemTemplate.CreateContent() as ViewCell); if (viewCellBinded == null) return null; viewCellBinded.BindingContext = item; return this.GetCell(collectionView, viewCellBinded, indexPath); } protected virtual UICollectionViewCell GetCell(UICollectionView collectionView, ViewCell item, NSIndexPath indexPath) { var collectionCell = collectionView.DequeueReusableCell(new NSString(GridViewCell.Key), indexPath) as GridViewCell; if (collectionCell == null) return null; collectionCell.ViewCell = item; return collectionCell; } protected override void OnElementChanged (ElementChangedEventArgs<GridView> e) { base.OnElementChanged (e); if (e.OldElement != null) { Unbind (e.OldElement); } if (e.NewElement != null) { if (Control == null) { var collectionView = new GridCollectionView() { AllowsMultipleSelection = false, SelectionEnable = e.NewElement.SelectionEnabled, ContentInset = new UIEdgeInsets ((float)this.Element.Padding.Top, (float)this.Element.Padding.Left, (float)this.Element.Padding.Bottom, (float)this.Element.Padding.Right), BackgroundColor = this.Element.BackgroundColor.ToUIColor (), ItemSize = new CoreGraphics.CGSize ((float)this.Element.ItemWidth, (float)this.Element.ItemHeight), RowSpacing = this.Element.RowSpacing, ColumnSpacing = this.Element.ColumnSpacing }; Bind (e.NewElement); collectionView.Source = this.DataSource; //collectionView.Delegate = this.GridViewDelegate; SetNativeControl (collectionView); } } } private void Unbind (GridView oldElement) { if (oldElement == null) return; oldElement.PropertyChanging -= this.ElementPropertyChanging; oldElement.PropertyChanged -= this.ElementPropertyChanged; var itemsSource = oldElement.ItemsSource as INotifyCollectionChanged; if (itemsSource != null) { itemsSource.CollectionChanged -= this.DataCollectionChanged; } } private void Bind (GridView newElement) { if (newElement == null) return; newElement.PropertyChanging += this.ElementPropertyChanging; newElement.PropertyChanged += this.ElementPropertyChanged; var source = newElement.ItemsSource as INotifyCollectionChanged; if (source != null) { source.CollectionChanged += this.DataCollectionChanged; } } private void ElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == GridView.ItemsSourceProperty.PropertyName) { var newItemsSource = this.Element.ItemsSource as INotifyCollectionChanged; if (newItemsSource != null) { newItemsSource.CollectionChanged += DataCollectionChanged; this.Control.ReloadData(); } } else if(e.PropertyName == "ItemWidth" || e.PropertyName == "ItemHeight") { this.Control.ItemSize = new CoreGraphics.CGSize ((float)this.Element.ItemWidth, (float)this.Element.ItemHeight); } } private void ElementPropertyChanging (object sender, PropertyChangingEventArgs e) { if (e.PropertyName == "ItemsSource") { var oldItemsSource = this.Element.ItemsSource as INotifyCollectionChanged; if (oldItemsSource != null) { oldItemsSource.CollectionChanged -= DataCollectionChanged; } } } private void DataCollectionChanged (object sender, NotifyCollectionChangedEventArgs e) { InvokeOnMainThread (()=> { try { if(this.Control == null) return; this.Control.ReloadData(); // TODO: try to handle add or remove operations gracefully, just reload the whole collection for other changes // InsertItems, DeleteItems or ReloadItems can cause // *** Assertion failure in -[XLabs_Forms_Controls_GridCollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:], // BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.30.14/UICollectionView.m:4324 // var indexes = new List<NSIndexPath>(); // switch (e.Action) { // case NotifyCollectionChangedAction.Add: // for (int i = 0; i < e.NewItems.Count; i++) { // indexes.Add(NSIndexPath.FromRowSection((nint)(e.NewStartingIndex + i),0)); // } // this.Control.InsertItems(indexes.ToArray()); // break; // case NotifyCollectionChangedAction.Remove: // for (int i = 0; i< e.OldItems.Count; i++) { // indexes.Add(NSIndexPath.FromRowSection((nint)(e.OldStartingIndex + i),0)); // } // this.Control.DeleteItems(indexes.ToArray()); // break; // default: // this.Control.ReloadData(); // break; // } } catch { } // todo: determine why we are hiding a possible exception here }); } private GridDataSource DataSource { get { return _dataSource ?? (_dataSource = new GridDataSource (GetCell, RowsInSection,ItemSelected)); } } protected override void Dispose (bool disposing) { base.Dispose (disposing); if (disposing && _dataSource != null) { Unbind (Element); _dataSource.Dispose (); _dataSource = null; } } } } 

For more information, click here for a custom class and click here for a rendering class.

0
source

Source: https://habr.com/ru/post/1264702/


All Articles