Billing/Billing.Shared/UI/GroupStackLayout.cs

193 lines
7.4 KiB
C#

using System.Collections;
using Xamarin.Forms;
namespace Billing.UI
{
public class GroupStackLayout : Layout<View>
{
public static readonly BindableProperty GroupHeaderTemplateProperty = BindableProperty.Create(nameof(GroupHeaderTemplate), typeof(DataTemplate), typeof(GroupStackLayout));
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(GroupStackLayout));
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(GroupStackLayout), propertyChanged: OnItemsSourcePropertyChanged);
public static readonly BindableProperty SpacingProperty = BindableProperty.Create(nameof(Spacing), typeof(double), typeof(GroupStackLayout), defaultValue: 4d);
public static readonly BindableProperty RowHeightProperty = BindableProperty.Create(nameof(RowHeight), typeof(double), typeof(GroupStackLayout), defaultValue: 32d);
public static readonly BindableProperty GroupHeightProperty = BindableProperty.Create(nameof(GroupHeight), typeof(double), typeof(GroupStackLayout), defaultValue: 24d);
public DataTemplate GroupHeaderTemplate
{
get => (DataTemplate)GetValue(GroupHeaderTemplateProperty);
set => SetValue(GroupHeaderTemplateProperty, value);
}
public DataTemplate ItemTemplate
{
get => (DataTemplate)GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
public IList ItemsSource
{
get => (IList)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public double Spacing
{
get => (double)GetValue(SpacingProperty);
set => SetValue(SpacingProperty, value);
}
public double RowHeight
{
get => (double)GetValue(RowHeightProperty);
set => SetValue(RowHeightProperty, value);
}
public double GroupHeight
{
get => (double)GetValue(GroupHeightProperty);
set => SetValue(GroupHeightProperty, value);
}
private static void OnItemsSourcePropertyChanged(BindableObject obj, object old, object @new)
{
var stack = (GroupStackLayout)obj;
stack.lastWidth = -1;
if (@new == null)
{
//stack.cachedLayout.Clear();
stack.Children.Clear();
stack.InvalidateLayout();
}
else if (@new is IList list)
{
stack.freezed = true;
//stack.cachedLayout.Clear();
stack.Children.Clear();
var groupTemplate = stack.GroupHeaderTemplate;
var itemTemplate = stack.ItemTemplate;
for (var i = 0; i < list.Count; i++)
{
var item = list[i];
if (item is IList sublist)
{
if (groupTemplate != null)
{
var child = groupTemplate.CreateContent();
if (child is View view)
{
view.BindingContext = item;
stack.Children.Add(view);
}
}
foreach (var it in sublist)
{
var child = itemTemplate.CreateContent();
if (child is View view)
{
view.BindingContext = it;
stack.Children.Add(view);
}
}
}
else
{
var child = itemTemplate.CreateContent();
if (child is View view)
{
view.BindingContext = list[i];
stack.Children.Add(view);
}
}
}
stack.freezed = false;
stack.UpdateChildrenLayout();
stack.InvalidateLayout();
}
}
//private readonly Dictionary<View, Rectangle> cachedLayout = new();
private bool freezed;
private double lastWidth = -1;
private SizeRequest lastSizeRequest;
public void Refresh(IList list)
{
OnItemsSourcePropertyChanged(this, null, list);
}
protected override void LayoutChildren(double x, double y, double width, double height)
{
if (freezed)
{
return;
}
var source = ItemsSource;
if (source == null || source.Count <= 0)
{
return;
}
var spacing = Spacing;
var lastHeight = 0d;
var rowHeight = RowHeight;
var groupHeight = GroupHeight;
foreach (var item in Children)
{
//var measured = item.Measure(width, height, MeasureFlags.IncludeMargins);
//var rect = new Rectangle(
// 0, lastHeight, width,
// measured.Request.Height);
//if (cachedLayout.TryGetValue(item, out var v))
//{
// if (v != rect)
// {
// cachedLayout[item] = rect;
// item.Layout(rect);
// }
//}
//else
//{
// cachedLayout.Add(item, rect);
// item.Layout(rect);
//}
double itemHeight;
if (item.BindingContext is IList)
{
itemHeight = groupHeight;
}
else
{
itemHeight = rowHeight;
}
var rect = new Rectangle(0, lastHeight, width, itemHeight);
//item.Layout(rect);
LayoutChildIntoBoundingRegion(item, rect);
lastHeight += itemHeight + spacing;
}
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (lastWidth == widthConstraint)
{
return lastSizeRequest;
}
lastWidth = widthConstraint;
var spacing = Spacing;
var lastHeight = 0d;
var rowHeight = RowHeight;
var groupHeight = GroupHeight;
foreach (var item in Children)
{
//var measured = item.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
//lastHeight += measured.Request.Height + spacing;
if (item.BindingContext is IList)
{
lastHeight += groupHeight + spacing;
}
else
{
lastHeight += rowHeight + spacing;
}
}
lastSizeRequest = new SizeRequest(new Size(widthConstraint, lastHeight));
return lastSizeRequest;
}
}
}