Skip to content

Adding StretchChild property to WrapPanel control #3005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
22 changes: 22 additions & 0 deletions Microsoft.Toolkit.Uwp.UI.Controls/WrapPanel/StretchChild.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.Toolkit.Uwp.UI.Controls
{
/// <summary>
/// Options for how to calculate the layout of <see cref="Windows.UI.Xaml.Controls.WrapGrid"/> items.
/// </summary>
public enum StretchChild
{
/// <summary>
/// Don't apply any additional stretching logic
/// </summary>
None,

/// <summary>
/// Make the last child stretch to fill the available space
/// </summary>
Last
}
}
93 changes: 65 additions & 28 deletions Microsoft.Toolkit.Uwp.UI.Controls/WrapPanel/WrapPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ public Thickness Padding
typeof(WrapPanel),
new PropertyMetadata(default(Thickness), LayoutPropertyChanged));

/// <summary>
/// Gets or sets a value indicating how to arrange child items
/// </summary>
public StretchChild StretchChild
{
get { return (StretchChild)GetValue(StretchChildProperty); }
set { SetValue(StretchChildProperty, value); }
}

/// <summary>
/// Identifies the <see cref="StretchChild"/> dependency property.
/// </summary>
/// <returns>The identifier for the <see cref="StretchChild"/> dependency property.</returns>
public static readonly DependencyProperty StretchChildProperty =
DependencyProperty.Register(
nameof(StretchChild),
typeof(StretchChild),
typeof(WrapPanel),
new PropertyMetadata(StretchChild.None, LayoutPropertyChanged));

private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WrapPanel wp)
Expand Down Expand Up @@ -179,42 +199,59 @@ protected override Size MeasureOverride(Size availableSize)
/// <inheritdoc />
protected override Size ArrangeOverride(Size finalSize)
{
var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height);
var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top);
var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom);
var position = new UvMeasure(Orientation, Padding.Left, Padding.Top);

double currentV = 0;
foreach (var child in Children)
if (Children.Count > 0)
{
var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
if (desiredMeasure.U == 0)
{
continue; // if an item is collapsed, avoid adding the spacing
}
var parentMeasure = new UvMeasure(Orientation, finalSize.Width, finalSize.Height);
var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top);
var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom);
var position = new UvMeasure(Orientation, Padding.Left, Padding.Top);

if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U)
double currentV = 0;
void arrange(UIElement child, bool isLast = false)
{
// next row!
position.U = paddingStart.U;
position.V += currentV + spacingMeasure.V;
currentV = 0;
}
var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
if (desiredMeasure.U == 0)
{
return; // if an item is collapsed, avoid adding the spacing
}

// place the item
if (Orientation == Orientation.Horizontal)
{
child.Arrange(new Rect(position.U, position.V, child.DesiredSize.Width, child.DesiredSize.Height));
if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U)
{
// next row!
position.U = paddingStart.U;
position.V += currentV + spacingMeasure.V;
currentV = 0;
}

// Stretch the last item to fill the available space
if (isLast)
{
desiredMeasure.U = parentMeasure.U - position.U;
}

// place the item
if (Orientation == Orientation.Horizontal)
{
child.Arrange(new Rect(position.U, position.V, desiredMeasure.U, desiredMeasure.V));
}
else
{
child.Arrange(new Rect(position.V, position.U, desiredMeasure.V, desiredMeasure.U));
}

// adjust the location for the next items
position.U += desiredMeasure.U + spacingMeasure.U;
currentV = Math.Max(desiredMeasure.V, currentV);
}
else

var lastIndex = Children.Count - 1;
for (var i = 0; i < lastIndex; i++)
{
child.Arrange(new Rect(position.V, position.U, child.DesiredSize.Width, child.DesiredSize.Height));
arrange(Children[i]);
}

// adjust the location for the next items
position.U += desiredMeasure.U + spacingMeasure.U;
currentV = Math.Max(desiredMeasure.V, currentV);
arrange(Children[lastIndex], StretchChild == StretchChild.Last);
}

return finalSize;
Expand Down