WrapLayout
This commit is contained in:
parent
7e314a634e
commit
2179c6a981
@ -38,6 +38,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\Definition.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\GroupStackLayout.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\ItemSelectPage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\WrapLayout.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Views\AccountPage.xaml.cs">
|
||||
<DependentUpon>AccountPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
216
Billing.Shared/UI/WrapLayout.cs
Normal file
216
Billing.Shared/UI/WrapLayout.cs
Normal file
@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Billing.UI
|
||||
{
|
||||
public class WrapLayout : Layout<View>
|
||||
{
|
||||
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(WrapLayout), propertyChanged: OnItemsSourcePropertyChanged);
|
||||
public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(nameof(ColumnSpacing), typeof(double), typeof(WrapLayout), defaultValue: 5.0, propertyChanged: (obj, _, _) => ((WrapLayout)obj).InvalidateLayout());
|
||||
public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(nameof(RowSpacing), typeof(double), typeof(WrapLayout), defaultValue: 5.0, propertyChanged: (obj, _, _) => ((WrapLayout)obj).InvalidateLayout());
|
||||
|
||||
private static void OnItemsSourcePropertyChanged(BindableObject obj, object old, object @new)
|
||||
{
|
||||
var itemTemplate = BindableLayout.GetItemTemplate(obj);
|
||||
if (itemTemplate == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var layout = (WrapLayout)obj;
|
||||
if (@new == null)
|
||||
{
|
||||
layout.Children.Clear();
|
||||
layout.InvalidateLayout();
|
||||
}
|
||||
else if (@new is IList list)
|
||||
{
|
||||
layout.freezed = true;
|
||||
layout.Children.Clear();
|
||||
foreach (var item in list)
|
||||
{
|
||||
var child = itemTemplate.CreateContent();
|
||||
if (child is View view)
|
||||
{
|
||||
view.BindingContext = item;
|
||||
layout.Children.Add(view);
|
||||
}
|
||||
}
|
||||
layout.freezed = false;
|
||||
layout.InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public IList ItemsSource
|
||||
{
|
||||
get => (IList)GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
public double ColumnSpacing
|
||||
{
|
||||
get => (double)GetValue(ColumnSpacingProperty);
|
||||
set => SetValue(ColumnSpacingProperty, value);
|
||||
}
|
||||
public double RowSpacing
|
||||
{
|
||||
get => (double)GetValue(RowSpacingProperty);
|
||||
set => SetValue(RowSpacingProperty, value);
|
||||
}
|
||||
|
||||
private readonly Dictionary<Size, LayoutData> layoutDataCache = new();
|
||||
|
||||
private bool freezed;
|
||||
|
||||
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
var data = GetLayoutData(widthConstraint, heightConstraint);
|
||||
if (data.VisibleChildrenCount == 0)
|
||||
{
|
||||
return new SizeRequest();
|
||||
}
|
||||
var totalSize = new Size(
|
||||
data.CellSize.Width * data.Columns + ColumnSpacing * (data.Columns - 1),
|
||||
data.CellSize.Height * data.Rows + RowSpacing * (data.Rows - 1));
|
||||
return new SizeRequest(totalSize);
|
||||
}
|
||||
|
||||
protected override void LayoutChildren(double x, double y, double width, double height)
|
||||
{
|
||||
if (freezed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data = GetLayoutData(width, height);
|
||||
if (data.VisibleChildrenCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double xChild = x;
|
||||
double yChild = y;
|
||||
int row = 0;
|
||||
int column = 0;
|
||||
|
||||
var columnSpacing = ColumnSpacing;
|
||||
var rowSpacing = RowSpacing;
|
||||
|
||||
foreach (View child in Children)
|
||||
{
|
||||
if (!child.IsVisible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), data.CellSize));
|
||||
if (++column == data.Columns)
|
||||
{
|
||||
column = 0;
|
||||
row++;
|
||||
xChild = x;
|
||||
yChild += rowSpacing + data.CellSize.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
xChild += columnSpacing + data.CellSize.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InvalidateLayout()
|
||||
{
|
||||
base.InvalidateLayout();
|
||||
layoutDataCache.Clear();
|
||||
}
|
||||
|
||||
protected override void OnChildMeasureInvalidated()
|
||||
{
|
||||
base.OnChildMeasureInvalidated();
|
||||
layoutDataCache.Clear();
|
||||
}
|
||||
|
||||
private LayoutData GetLayoutData(double width, double height)
|
||||
{
|
||||
Size size = new(width, height);
|
||||
if (layoutDataCache.ContainsKey(size))
|
||||
{
|
||||
return layoutDataCache[size];
|
||||
}
|
||||
|
||||
int visibleChildrenCount = 0;
|
||||
Size maxChildSize = new();
|
||||
LayoutData layoutData;
|
||||
|
||||
foreach (View child in Children)
|
||||
{
|
||||
if (!child.IsVisible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
visibleChildrenCount++;
|
||||
SizeRequest childSizeRequest = child.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
maxChildSize.Width = Math.Max(maxChildSize.Width, childSizeRequest.Request.Width);
|
||||
maxChildSize.Height = Math.Max(maxChildSize.Height, childSizeRequest.Request.Height);
|
||||
}
|
||||
|
||||
if (visibleChildrenCount > 0)
|
||||
{
|
||||
var columnSpacing = ColumnSpacing;
|
||||
int rows;
|
||||
int columns;
|
||||
if (double.IsPositiveInfinity(width))
|
||||
{
|
||||
columns = visibleChildrenCount;
|
||||
rows = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
columns = (int)((width + columnSpacing) / (maxChildSize.Width + columnSpacing));
|
||||
columns = Math.Max(1, columns);
|
||||
rows = (visibleChildrenCount + columns - 1) / columns;
|
||||
}
|
||||
Size cellSize = new();
|
||||
if (double.IsPositiveInfinity(width))
|
||||
{
|
||||
cellSize.Width = maxChildSize.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellSize.Width = (width - columnSpacing * (columns - 1)) / columns;
|
||||
}
|
||||
if (double.IsPositiveInfinity(height))
|
||||
{
|
||||
cellSize.Height = maxChildSize.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellSize.Height = (height - RowSpacing * (rows - 1)) / rows;
|
||||
}
|
||||
layoutData = new LayoutData(visibleChildrenCount, cellSize, rows, columns);
|
||||
}
|
||||
else
|
||||
{
|
||||
layoutData = new LayoutData();
|
||||
}
|
||||
|
||||
layoutDataCache.Add(size, layoutData);
|
||||
return layoutData;
|
||||
}
|
||||
}
|
||||
|
||||
public class LayoutData
|
||||
{
|
||||
public int VisibleChildrenCount { get; set; }
|
||||
public Size CellSize { get; set; }
|
||||
public int Rows { get; set; }
|
||||
public int Columns { get; set; }
|
||||
|
||||
public LayoutData() { }
|
||||
public LayoutData(int count, Size size, int rows, int columns)
|
||||
{
|
||||
VisibleChildrenCount = count;
|
||||
CellSize = size;
|
||||
Rows = rows;
|
||||
Columns = columns;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user