1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
| /// <summary>
/// A layout that create lines / rows.
/// </summary>
/// <seealso cref="Xamarin.Forms.Layout{Xamarin.Forms.View}" />
/// <autogeneratedoc />
public class InlineWrapLayout : Layout<View>
{
private readonly Dictionary<Size, LayoutData> layoutDataCache = new Dictionary<Size, LayoutData>();
public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(
"ColumnSpacing",
typeof(double),
typeof(InlineWrapLayout),
5.0,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
((InlineWrapLayout)bindable).InvalidateLayout();
});
public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(
"RowSpacing",
typeof(double),
typeof(InlineWrapLayout),
5.0,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
((InlineWrapLayout)bindable).InvalidateLayout();
});
/// <summary>
/// Gets or sets the column spacing.
/// </summary>
/// <value>
/// The column spacing.
/// </value>
/// <autogeneratedoc />
public double ColumnSpacing
{
set { SetValue(ColumnSpacingProperty, value); }
get { return (double)GetValue(ColumnSpacingProperty); }
}
/// <summary>
/// Gets or sets the row spacing.
/// </summary>
/// <value>
/// The row spacing.
/// </value>
/// <autogeneratedoc />
public double RowSpacing
{
set { SetValue(RowSpacingProperty, value); }
get { return (double)GetValue(RowSpacingProperty); }
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
var layoutData = GetLayoutData(widthConstraint, heightConstraint);
if (layoutData.VisibleChildCount == 0)
{
return new SizeRequest();
}
return new SizeRequest(layoutData.Size);
}
/// <summary>
/// Positions and sizes the children of a Layout.
/// </summary>
/// <param name="x">A value representing the x coordinate of the child region bounding box.</param>
/// <param name="y">A value representing the y coordinate of the child region bounding box.</param>
/// <param name="width">A value representing the width of the child region bounding box.</param>
/// <param name="height">A value representing the height of the child region bounding box.</param>
/// <remarks>
/// Implementors wishing to change the default behavior of a Layout should override this method. It is suggested to still call the base method and modify its calculated results.
/// </remarks>
/// <autogeneratedoc />
protected override void LayoutChildren(double x, double y, double width, double height)
{
var layoutData = GetLayoutData(width, height);
if (layoutData.VisibleChildCount == 0)
{
return;
}
double xChild = x;
double yChild = y;
int row = 0;
int column = 0;
foreach (var child in Children)
{
if (!child.IsVisible)
{
continue;
}
LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), layoutData.ChildrenSizesRows[row][column]));
if (column >= layoutData.ChildrenSizesRows[row].Count - 1)
{
xChild = x;
yChild += RowSpacing + layoutData.ChildrenSizesRows[row].Max(s => s.Height);
row++;
column = 0;
}
else
{
xChild += ColumnSpacing + layoutData.ChildrenSizesRows[row][column].Width;
column++;
}
}
}
private LayoutData GetLayoutData(double width, double height)
{
var size = new Size(width, height);
// Check if cached information is available.
if (layoutDataCache.ContainsKey(size))
{
return layoutDataCache[size];
}
int visibleChildCount = 0;
var currentChildrenSizesRow = new List<Size>();
var childrenSizesRows = new List<List<Size>> { currentChildrenSizesRow };
// Enumerate through all the children.
foreach (View child in Children)
{
// Skip invisible children.
if (!child.IsVisible)
continue;
// Count the visible children.
visibleChildCount++;
// Get the child's requested size.
var childSizeRequest = child.Measure(Double.PositiveInfinity, Double.PositiveInfinity);
if (childSizeRequest.Minimum.Width > width)
throw new Exception($"L'element {child.GetType()} est trop large {childSizeRequest.Request.Width} vs {width} demandés");
// la vue ne tient pas -> passer à la suivante (on utilise la taille minimum)
if (currentChildrenSizesRow.Sum(s => s.Width + ColumnSpacing) + childSizeRequest.Request.Width > width && currentChildrenSizesRow.Count > 0)
{
currentChildrenSizesRow = new List<Size>();
childrenSizesRows.Add(currentChildrenSizesRow);
};
if (childSizeRequest.Request.Width <= width)
currentChildrenSizesRow.Add(childSizeRequest.Request);
else
currentChildrenSizesRow.Add(new Size(width, childSizeRequest.Request.Height));
}
double totalWidth = 0;
double totalHeight = 0;
if (visibleChildCount != 0)
{
// Calculate the number of rows and columns.
if (Double.IsPositiveInfinity(width))
{
childrenSizesRows = new List<List<Size>>
{
new List<Size>(childrenSizesRows.SelectMany(r => r))
};
}
totalWidth = Math.Max(0, childrenSizesRows.Max(r => r.Sum(s => s.Width + ColumnSpacing) - ColumnSpacing));
totalHeight = Math.Max(0, childrenSizesRows.Sum(r => r.Count > 0 ? r.Max(s => s.Height) + RowSpacing : 0) - RowSpacing);
}
var totalSize = new Size(totalWidth, totalHeight);
var layoutData = new LayoutData(visibleChildCount, childrenSizesRows, totalSize);
layoutDataCache.Add(size, layoutData);
return layoutData;
}
/// <summary>
/// Invalidates the current layout.
/// </summary>
/// <remarks>
/// Calling this method will invalidate the measure and triggers a new layout cycle.
/// </remarks>
/// <autogeneratedoc />
protected override void InvalidateLayout()
{
base.InvalidateLayout();
// Discard all layout information for children added or removed.
layoutDataCache.Clear();
}
/// <summary>
/// Invoked whenever a child of the layout has emitted <see cref="E:Xamarin.Forms.VisualElement.MeaureInvalidated" />. Implement this method to add class handling for this event.
/// </summary>
/// <autogeneratedoc />
protected override void OnChildMeasureInvalidated()
{
base.OnChildMeasureInvalidated();
// Discard all layout information for child size changed.
layoutDataCache.Clear();
}
private struct LayoutData
{
public int VisibleChildCount { get; private set; }
public Size Size { get; private set; }
public List<List<Size>> ChildrenSizesRows { get; private set; }
public LayoutData(int visibleChildCount, List<List<Size>> childrenSizesRows, Size size) : this()
{
ChildrenSizesRows = childrenSizesRows;
VisibleChildCount = visibleChildCount;
Size = size;
}
public Size CellSize { get; private set; }
public int Rows { get; private set; }
public int Columns { get; private set; }
public LayoutData(int visibleChildCount, Size cellSize, int rows, int columns) : this()
{
VisibleChildCount = visibleChildCount;
CellSize = cellSize;
Rows = rows;
Columns = columns;
}
}
} |
Partager