Skip to content
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

Refactor: Adaptive Column Width for StaggeredLayout #371

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions components/Primitives/samples/StaggeredLayoutSample.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- 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. -->
<!-- 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. -->
<Page x:Class="PrimitivesExperiment.Samples.StaggeredLayoutSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand All @@ -9,21 +9,21 @@
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="StaggeredTemplate">
<DataTemplate x:Key="StaggeredTemplate"
x:DataType="local:ColorItem">
<Grid Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}">
<Border Width="{Binding Width}"
Height="{Binding Height}"
<Border Height="{x:Bind Height}"
Margin="4"
CornerRadius="{StaticResource ControlCornerRadius}">
<Border.Background>
<SolidColorBrush Color="{Binding Color}" />
<SolidColorBrush Color="{x:Bind Color}" />
</Border.Background>
<TextBlock Margin="6,4,4,4"
FontSize="16"
Text="{Binding Index}" />
Text="{x:Bind Index}" />
</Border>
</Grid>

Expand All @@ -37,6 +37,7 @@
<muxc:ItemsRepeater.Layout>
<controls:StaggeredLayout ColumnSpacing="{x:Bind ColumnSpacing, Mode=OneWay}"
DesiredColumnWidth="{x:Bind DesiredColumnWidth, Mode=OneWay}"
ItemsStretch="{x:Bind local:StaggeredLayoutSample.ConvertStringToStaggeredLayoutItemsStretch(ItemsStretchProperty), Mode=OneWay}"
RowSpacing="{x:Bind RowSpacing, Mode=OneWay}" />
</muxc:ItemsRepeater.Layout>
</muxc:ItemsRepeater>
Expand Down
12 changes: 10 additions & 2 deletions components/Primitives/samples/StaggeredLayoutSample.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ namespace PrimitivesExperiment.Samples;
[ToolkitSampleNumericOption("DesiredColumnWidth", initial: 250, min: 50, max: 400, step: 1, Title = "DesiredColumnWidth")]
[ToolkitSampleNumericOption("ColumnSpacing", initial: 5, min: 0, max: 50, step: 1, Title = "ColumnSpacing")]
[ToolkitSampleNumericOption("RowSpacing", initial: 5, min: 0, max: 50, step: 1, Title = "RowSpacing")]
[ToolkitSampleMultiChoiceOption("ItemsStretchProperty", "None", "Fill", Title = "ItemsStretch")]

[ToolkitSample(id: nameof(StaggeredLayoutSample), "StaggeredPanel", description: $"A sample for showing how to create and use a {nameof(StaggeredLayout)}.")]
[ToolkitSample(id: nameof(StaggeredLayoutSample), "StaggeredLayout", description: $"A sample for showing how to create and use a {nameof(StaggeredLayout)}.")]
public sealed partial class StaggeredLayoutSample : Page
{
public ObservableCollection<ColorItem> ColorsCollection = new();
Expand All @@ -24,10 +25,17 @@ public StaggeredLayoutSample()
random = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < random.Next(100, 200); i++)
{
var item = new ColorItem { Index = i, Width = random.Next(50, 250), Height = random.Next(50, 250), Color = Color.FromArgb(255, (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)) };
var item = new ColorItem { Index = i, Height = random.Next(50, 250), Color = Color.FromArgb(255, (byte)random.Next(0, 255), (byte)random.Next(0, 255), (byte)random.Next(0, 255)) };
ColorsCollection.Add(item);
}
}

public static StaggeredLayoutItemsStretch ConvertStringToStaggeredLayoutItemsStretch(string itemStretch) => itemStretch switch
{
"None" => StaggeredLayoutItemsStretch.None,
"Fill" => StaggeredLayoutItemsStretch.Fill,
_ => throw new System.NotImplementedException(),
};
}

public class ColorItem
Expand Down
14 changes: 7 additions & 7 deletions components/Primitives/samples/StaggeredPanelSample.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- 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. -->
<!-- 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. -->
<Page x:Class="PrimitivesExperiment.Samples.StaggeredPanelSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand All @@ -10,24 +10,24 @@
x:Name="ThisSamplePage">

<Page.Resources>
<DataTemplate x:Key="StaggeredTemplate">
<DataTemplate x:Key="StaggeredTemplate"
x:DataType="local:ColorItem">
<Grid Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}">
<Border Width="{Binding Width}"
Height="{Binding Height}"
<Border Width="{x:Bind Width}"
Height="{x:Bind Height}"
Margin="4"
CornerRadius="{StaticResource ControlCornerRadius}">
<Border.Background>
<SolidColorBrush Color="{Binding Color}" />
<SolidColorBrush Color="{x:Bind Color}" />
</Border.Background>
<TextBlock Margin="6,4,4,4"
FontSize="16"
Text="{Binding Index}" />
Text="{x:Bind Index}" />
</Border>
</Grid>

</DataTemplate>
</Page.Resources>

Expand Down
2 changes: 1 addition & 1 deletion components/Primitives/samples/StaggeredPanelSample.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace PrimitivesExperiment.Samples;
[ToolkitSampleNumericOption("ColumnSpacing", initial: 5, min: 0, max: 50, step: 1, Title = "ColumnSpacing")]
[ToolkitSampleNumericOption("RowSpacing", initial: 5, min: 0, max: 50, step: 1, Title = "RowSpacing")]

[ToolkitSample(id: nameof(StaggeredPanelSample), "WrapPanel", description: $"A sample for showing how to create and use a {nameof(StaggeredPanel)}.")]
[ToolkitSample(id: nameof(StaggeredPanelSample), "StaggeredPanel", description: $"A sample for showing how to create and use a {nameof(StaggeredPanel)}.")]
public sealed partial class StaggeredPanelSample : Page
{
public ObservableCollection<ColorItem> ColorsCollection = new();
Expand Down
55 changes: 48 additions & 7 deletions components/Primitives/src/StaggeredLayout/StaggeredLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,25 @@ public StaggeredLayout()
/// <summary>
/// Gets or sets the desired width for each column.
/// </summary>
/// <remarks>
/// The width of columns can exceed the DesiredColumnWidth if the HorizontalAlignment is set to Stretch.
/// </remarks>
public StaggeredLayoutItemsStretch ItemsStretch
{
get => (StaggeredLayoutItemsStretch)GetValue(ItemsStretchProperty);
set => SetValue(ItemsStretchProperty, value);
}

/// <summary>
/// Identifies the <see cref="ItemsStretch"/> dependency property.
/// </summary>
/// <returns>The identifier for the <see cref="DesiredColumnWidth"/> dependency property.</returns>
public static readonly DependencyProperty ItemsStretchProperty = DependencyProperty.Register(
nameof(ItemsStretch),
typeof(StaggeredLayoutItemsStretch),
typeof(StaggeredLayout),
new PropertyMetadata(StaggeredLayoutItemsStretch.None, OnItemsStretchChanged));

/// <summary>
/// Gets or sets the desired width for each column.
/// </summary>
public double DesiredColumnWidth
{
get { return (double)GetValue(DesiredColumnWidthProperty); }
Expand Down Expand Up @@ -142,16 +158,35 @@ protected override Size MeasureOverride(VirtualizingLayoutContext context, Size
double availableHeight = availableSize.Height;

// This ternary prevents the column width from being NaN, which would otherwise cause an exception when measuring item sizes
double columnWidth = double.IsNaN(DesiredColumnWidth) ? availableWidth : Math.Min(DesiredColumnWidth, availableWidth);
double columnWidth;
int numColumns;
if (ItemsStretch is StaggeredLayoutItemsStretch.None)
{
columnWidth = double.IsNaN(DesiredColumnWidth) ? availableWidth : Math.Min(DesiredColumnWidth, availableWidth);
numColumns = Math.Max(1, (int)Math.Floor(availableWidth / state.ColumnWidth));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦙 Leaving a marker here for historical record/the future that we had missed the state.ColumnWidth here not being the desired value of columnWidth calculated above, as @Poker-sang called out here: #542 (comment)

}
else
{
if (double.IsNaN(DesiredColumnWidth) || DesiredColumnWidth > availableWidth)
{
columnWidth = availableWidth;
numColumns = 1;
}
else
{
var tempAvailableWidth = availableWidth + ColumnSpacing;
numColumns = (int)Math.Floor(tempAvailableWidth / DesiredColumnWidth);
columnWidth = tempAvailableWidth / numColumns - ColumnSpacing;
}
}

if (columnWidth != state.ColumnWidth)
{
// The items will need to be remeasured
state.Clear();
}

// This ternary prevents the column width from being NaN, which would otherwise cause an exception when measuring item sizes
state.ColumnWidth = double.IsNaN(DesiredColumnWidth) ? availableWidth : Math.Min(DesiredColumnWidth, availableWidth);
int numColumns = Math.Max(1, (int)Math.Floor(availableWidth / state.ColumnWidth));
state.ColumnWidth = columnWidth;

// adjust for column spacing on all columns expect the first
double totalWidth = state.ColumnWidth + ((numColumns - 1) * (state.ColumnWidth + ColumnSpacing));
Expand Down Expand Up @@ -293,6 +328,12 @@ protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size
return finalSize;
}

private static void OnItemsStretchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var panel = (StaggeredLayout)d;
panel.InvalidateMeasure();
}

private static void OnDesiredColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var panel = (StaggeredLayout)d;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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 CommunityToolkit.WinUI.Controls;

/// <summary>
/// Defines constants that specify how items are sized to fill the available space in a <see cref="StaggeredLayout"/>.
/// </summary>
public enum StaggeredLayoutItemsStretch
{
/// <summary>
/// The items' width is determined by the <see cref="StaggeredLayout.DesiredColumnWidth"/>.
/// </summary>
None,

/// <summary>
/// The items' width is determined by the parent's width. The minimum width is determined by the <see cref="StaggeredLayout.DesiredColumnWidth"/>.
/// </summary>
Fill
}
Loading