diff --git a/components/Primitives/samples/StaggeredLayoutSample.xaml b/components/Primitives/samples/StaggeredLayoutSample.xaml index ba5a91a7..692427da 100644 --- a/components/Primitives/samples/StaggeredLayoutSample.xaml +++ b/components/Primitives/samples/StaggeredLayoutSample.xaml @@ -1,4 +1,4 @@ - + - + - - + + Text="{x:Bind Index}" /> @@ -37,6 +37,7 @@ diff --git a/components/Primitives/samples/StaggeredLayoutSample.xaml.cs b/components/Primitives/samples/StaggeredLayoutSample.xaml.cs index d4eea6c7..c9580548 100644 --- a/components/Primitives/samples/StaggeredLayoutSample.xaml.cs +++ b/components/Primitives/samples/StaggeredLayoutSample.xaml.cs @@ -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 ColorsCollection = new(); @@ -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 diff --git a/components/Primitives/samples/StaggeredPanelSample.xaml b/components/Primitives/samples/StaggeredPanelSample.xaml index 40eba9cd..f72031fc 100644 --- a/components/Primitives/samples/StaggeredPanelSample.xaml +++ b/components/Primitives/samples/StaggeredPanelSample.xaml @@ -1,4 +1,4 @@ - + - + - - + + Text="{x:Bind Index}" /> - diff --git a/components/Primitives/samples/StaggeredPanelSample.xaml.cs b/components/Primitives/samples/StaggeredPanelSample.xaml.cs index fdbcdd47..dab37fb0 100644 --- a/components/Primitives/samples/StaggeredPanelSample.xaml.cs +++ b/components/Primitives/samples/StaggeredPanelSample.xaml.cs @@ -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 ColorsCollection = new(); diff --git a/components/Primitives/src/StaggeredLayout/StaggeredLayout.cs b/components/Primitives/src/StaggeredLayout/StaggeredLayout.cs index 43f316be..9e35fa36 100644 --- a/components/Primitives/src/StaggeredLayout/StaggeredLayout.cs +++ b/components/Primitives/src/StaggeredLayout/StaggeredLayout.cs @@ -22,9 +22,25 @@ public StaggeredLayout() /// /// Gets or sets the desired width for each column. /// - /// - /// The width of columns can exceed the DesiredColumnWidth if the HorizontalAlignment is set to Stretch. - /// + public StaggeredLayoutItemsStretch ItemsStretch + { + get => (StaggeredLayoutItemsStretch)GetValue(ItemsStretchProperty); + set => SetValue(ItemsStretchProperty, value); + } + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty ItemsStretchProperty = DependencyProperty.Register( + nameof(ItemsStretch), + typeof(StaggeredLayoutItemsStretch), + typeof(StaggeredLayout), + new PropertyMetadata(StaggeredLayoutItemsStretch.None, OnItemsStretchChanged)); + + /// + /// Gets or sets the desired width for each column. + /// public double DesiredColumnWidth { get { return (double)GetValue(DesiredColumnWidthProperty); } @@ -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)); + } + 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)); @@ -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; diff --git a/components/Primitives/src/StaggeredLayout/StaggeredLayoutItemsStretch.cs b/components/Primitives/src/StaggeredLayout/StaggeredLayoutItemsStretch.cs new file mode 100644 index 00000000..e03d707d --- /dev/null +++ b/components/Primitives/src/StaggeredLayout/StaggeredLayoutItemsStretch.cs @@ -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; + +/// +/// Defines constants that specify how items are sized to fill the available space in a . +/// +public enum StaggeredLayoutItemsStretch +{ + /// + /// The items' width is determined by the . + /// + None, + + /// + /// The items' width is determined by the parent's width. The minimum width is determined by the . + /// + Fill +}