Skip to content

Instantly share code, notes, and snippets.

@rc1021
Created March 29, 2017 05:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rc1021/e3b8fa6641d2e5d358a9963a8f201df4 to your computer and use it in GitHub Desktop.
Save rc1021/e3b8fa6641d2e5d358a9963a8f201df4 to your computer and use it in GitHub Desktop.
Custom layouts with Xamarin.Forms: auto arrangement cells (not finish)
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1"
x:Class="App1.AutoArrangement"
Title="Auto-Arrangement">
<ContentPage.Content>
<local:GridListView
ItemHeight="100"
RowSpacing="10"
ColumnSpacing="5"
MaxItemsPerRow="3"
ItemsSource="{Binding Source}">
<local:GridListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding .}" />
</DataTemplate>
</local:GridListView.ItemTemplate>
</local:GridListView>
</ContentPage.Content>
</ContentPage>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using Xamarin.Forms;
using CarouselView.FormsPlugin.Abstractions;
using Cowell.App.WebService.Near;
namespace App1.Pages
{
using Cowell.App.Extensions;
public partial class AutoArrangement : ContentPage
{
public ObservableCollection<string> Sources { set; get; }
public NearInfoPage()
{
Source = new ObservableCollection<string>
{
"Row 1.",
"Row 2.",
"Row 3.",
"Row 4.",
"Row 5.",
};
InitializeComponent();
BindingContext = this;
}
}
}
/**
* Refference: http://xfcomplete.net/xamarin.forms/2016/01/13/creating-custom-layouts-with-xamarinforms/
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
namespace App1
{
public class GridListView : Layout<View>
{
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create<GridListView, IEnumerable>(o => o.ItemsSource, default(IEnumerable), propertyChanged: OnItemsSourceChanged);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
private static void OnItemsSourceChanged(BindableObject bindable, IEnumerable oldvalue, IEnumerable newvalue)
{
var view = (GridListView)bindable;
view.ReCreateChildrens();
}
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create<GridListView, DataTemplate>(o => o.ItemTemplate, default(DataTemplate), propertyChanged: OnItemsTemplateChanged);
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
private static void OnItemsTemplateChanged(BindableObject bindable, DataTemplate oldvalue, DataTemplate newvalue)
{
var view = (GridListView)bindable;
view.ReCreateChildrens();
}
public int MaxItemsPerRow { get; set; }
public int ItemHeight { get; set; }
public double RowSpacing { get; set; }
public double ColumnSpacing { get; set; }
public GridListView()
{
}
private void ReCreateChildrens()
{
if (ItemsSource == null || ItemTemplate == null)
return;
foreach (var item in ItemsSource)
{
var view = ItemTemplate.CreateContent() as View;
view.BindingContext = item;
Children.Add(view);
}
}
protected override void LayoutChildren(double x, double y, double width, double height)
{
var colWidth = width / MaxItemsPerRow;
for (int i = 0; i < Children.Count; i++)
{
var child = Children[i];
if (!child.IsVisible)
continue;
var virtualColumn = i % MaxItemsPerRow;
var virtualRow = i / MaxItemsPerRow;
var rowSpacing = (virtualRow != 0) ? RowSpacing : 0;
var colSpacing = (virtualColumn != 0) ? ColumnSpacing : 0;
var childX = x + (colWidth + colSpacing) * virtualColumn;
var childY = y + (ItemHeight + rowSpacing) * virtualRow;
LayoutChildIntoBoundingRegion(child, new Rectangle(childX, childY, colWidth, ItemHeight));
}
}
protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
{
// Check our cache for existing results
SizeRequest cachedResult;
var constraintSize = new Size(widthConstraint, heightConstraint);
if (_measureCache.TryGetValue(constraintSize, out cachedResult))
{
return cachedResult;
}
var height = 0.0;
var minHeight = 0.0;
var width = 0.0;
var minWidth = 0.0;
var visibleChildrensCount = (double)Children.Count(c => c.IsVisible);
var rowsCount = Math.Ceiling(visibleChildrensCount / MaxItemsPerRow);
height = minHeight = (ItemHeight + RowSpacing) * rowsCount - RowSpacing;
width = minWidth = widthConstraint;
// store our result in the cache for next time
var result = new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
_measureCache[constraintSize] = result;
return result;
}
protected override void InvalidateMeasure()
{
_measureCache.Clear();
base.InvalidateMeasure();
}
readonly Dictionary<Size, SizeRequest> _measureCache = new Dictionary<Size, SizeRequest>();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment