194 lines
7.3 KiB
C#
194 lines
7.3 KiB
C#
using System.Collections;
|
|
using Xamarin.Forms;
|
|
|
|
namespace Billing.UI
|
|
{
|
|
public class GroupStackLayout : Layout<View>
|
|
{
|
|
public static readonly BindableProperty GroupHeaderTemplateProperty = Helper.Create<DataTemplate, GroupStackLayout>(nameof(GroupHeaderTemplate));
|
|
public static readonly BindableProperty ItemTemplateProperty = Helper.Create<DataTemplate, GroupStackLayout>(nameof(ItemTemplate));
|
|
public static readonly BindableProperty ItemsSourceProperty = Helper.Create<IList, GroupStackLayout>(nameof(ItemsSource), propertyChanged: OnItemsSourcePropertyChanged);
|
|
public static readonly BindableProperty SpacingProperty = Helper.Create<double, GroupStackLayout>(nameof(Spacing), defaultValue: 4d);
|
|
public static readonly BindableProperty RowHeightProperty = Helper.Create<double, GroupStackLayout>(nameof(RowHeight), defaultValue: 32d);
|
|
public static readonly BindableProperty GroupHeightProperty = Helper.Create<double, GroupStackLayout>(nameof(GroupHeight), 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(GroupStackLayout stack, IList old, IList list)
|
|
{
|
|
stack.lastWidth = -1;
|
|
if (list == null)
|
|
{
|
|
//stack.cachedLayout.Clear();
|
|
stack.Children.Clear();
|
|
stack.InvalidateLayout();
|
|
}
|
|
else
|
|
{
|
|
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 padding = Padding;
|
|
var spacing = Spacing;
|
|
var lastHeight = padding.Top;
|
|
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(padding.Left, 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;
|
|
}
|
|
}
|
|
var padding = Padding;
|
|
lastSizeRequest = new SizeRequest(new Size(widthConstraint, lastHeight + padding.Top + padding.Bottom));
|
|
return lastSizeRequest;
|
|
}
|
|
}
|
|
}
|