Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
99c07b3
StretchPanel initial commit
Avid29 Nov 29, 2025
e53c206
Basic StretchPanel implementation
Avid29 Nov 29, 2025
be5f590
Removed Wrap property
Avid29 Nov 29, 2025
c4b9ddc
Improved alignment behavior
Avid29 Nov 29, 2025
4966664
Merge branch 'CommunityToolkit:main' into StretchPanel
Avid29 Nov 29, 2025
dc2844e
Improved Sample to better display 600px vertical sample
Avid29 Nov 29, 2025
46703b9
Added FixedRowLengths property
Avid29 Nov 29, 2025
62b5e20
Added ForcedStretchMethod property and enum
Avid29 Nov 29, 2025
5c8a3d8
Fixed alignment issues for forced stretching
Avid29 Nov 29, 2025
a76e62f
Refactored Arrange to operate by rows
Avid29 Nov 30, 2025
379769c
Added Markdown Docs for the StretchPanel
Avid29 Nov 30, 2025
4977f87
Added OverflowBehaviors for StretchPanel
Avid29 Dec 1, 2025
a05b71d
Renamed to WrapPanel2
Avid29 Dec 3, 2025
f02d885
Ran XAML styles
Avid29 Dec 3, 2025
7716c08
Replaced Horizontal/Vertical Spacing with Item/Line spacing
Avid29 Dec 3, 2025
811bf3b
Merge branch 'main' into WrapPanel2
Avid29 Dec 9, 2025
8ccafec
Rennamed ForceStretchMethod ro StretchChildren
Avid29 Dec 9, 2025
c3d5658
Fixed missed rename in WrapPanel markdown docs
Avid29 Dec 9, 2025
f3fe793
Revised WrapPanel2 samples
Avid29 Dec 10, 2025
8d64ee6
Applied XAML styles
Avid29 Dec 10, 2025
d2408cb
Fixed FixedRowLength behavior
Avid29 Dec 10, 2025
349686c
Fixed ProportionalSample reference in Markdown docs
Avid29 Dec 10, 2025
63e1196
Fixed measuring issue causing FixedLengthRows with equally stretched …
Avid29 Dec 10, 2025
7478e2d
Merge branch 'main' into WrapPanel2
Arlodotexe Dec 12, 2025
3524ee9
Merge branch 'main' into WrapPanel2
Avid29 Dec 12, 2025
d44ef19
Addressed simple changes from @michael-hawker review
Avid29 Dec 16, 2025
701f749
XAML styles
Avid29 Dec 16, 2025
901fc13
Added behavior to perform justification through spacing instead of st…
Avid29 Dec 16, 2025
134e71c
Updated comments
Avid29 Dec 17, 2025
6af1a89
Fixed mismatched property and DependencyProperty name
Avid29 Dec 17, 2025
e512d6c
Changed ItemsJustification to an enum
Avid29 Dec 18, 2025
702b181
Fixed outdated WrapPanel2 measurements on alignment changed
Avid29 Dec 18, 2025
18328c0
Merge branch 'main' into WrapPanel2
Avid29 Jan 7, 2026
d8be9f2
Merge branch 'main' into WrapPanel2
Avid29 Jan 8, 2026
ba0ee31
Merge branch 'main' into WrapPanel2
Avid29 Feb 5, 2026
22826af
Merge branch 'main' into WrapPanel2
Avid29 Feb 9, 2026
4e8ed94
Apply suggestions from code review
michael-hawker Mar 4, 2026
1726f3d
Improved WrapPanel2 docs
Avid29 Mar 18, 2026
73abf09
Replaced tables with headered sections in WrapPanel2 docs
Avid29 Mar 18, 2026
f36b765
Renamed the project StretchPanel to WrapPanel2
Avid29 Mar 29, 2026
ef13bd1
Merge branch 'main' into WrapPanel2
Avid29 Mar 29, 2026
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
3 changes: 3 additions & 0 deletions components/WrapPanel2/OpenSolution.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@ECHO OFF

powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %*
Binary file added components/WrapPanel2/samples/Assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions components/WrapPanel2/samples/Dependencies.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!--
WinUI 2 under UWP uses TargetFramework uap10.0.*
WinUI 3 under WinAppSdk uses TargetFramework net6.0-windows10.*
However, under Uno-powered platforms, both WinUI 2 and 3 can share the same TargetFramework.

MSBuild doesn't play nicely with this out of the box, so we've made it easy for you.

For .NET Standard packages, you can use the Nuget Package Manager in Visual Studio.
For UWP / WinAppSDK / Uno packages, place the package references here.
-->
<Project>
<!-- WinUI 2 / UWP -->
<ItemGroup Condition="'$(IsUwp)' == 'true'">
<!-- <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>

<!-- WinUI 2 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '2'">
<!-- <PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.11"/> -->
</ItemGroup>

<!-- WinUI 3 / WinAppSdk -->
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
<!-- <PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>

<!-- WinUI 3 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '3'">
<!-- <PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.100-dev.15.g12261e2626"/> -->
</ItemGroup>
</Project>
30 changes: 30 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2.Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />

<PropertyGroup>
<ToolkitComponentName>WrapPanel2</ToolkitComponentName>
</PropertyGroup>

<!-- Sets this up as a toolkit component's sample project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SampleProject.props" />
<ItemGroup>
<Compile Update="WrapPanel2BasicSample.xaml.cs">
<DependentUpon>WrapPanel2BasicSample.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="WrapPanel2ProportionalSample.xaml.cs">
<DependentUpon>WrapPanel2ProportionalSample.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="WrapPanel2MegaSample.xaml.cs">
<DependentUpon>WrapPanel2MegaSample.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Content Update="WrapPanel2BasicSample.xaml">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
</Project>
92 changes: 92 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: WrapPanel2
author: Avid29
description: A labs-component candidate for a new WrapPanel implementation.
keywords: WrapPanel, Control, Layout
dev_langs:
- csharp
category: Layouts
subcategory: Panel
discussion-id: 762
issue-id: 763
icon: assets/icon.png
---

# WrapPanel2

The WrapPanel2 is an advanced layout control that uses `GridLength` definitions to manage item sizing within a wrapping flow. It provides granular control over how items occupy space, particularly when using proportional (Star) sizing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could be good to add more here about how this extends/differs from the original WrapPanel.

(We should decide if one of our goals is drop-in upgrade or not, but that can come later.)

## Proportional Sizing Logic

The behavior of items with **Star** 'LayoutLength' values depends on the panel's justification:

**When Stretched/Distributed:** If `ItemsJustification` is set to a distribution mode (like `SpaceBetween`) or if the panel is stretched along the orientation axis, Star-sized items proportionally occupy the available remaining space.

**When Aligned (Start, Center, End):** Star-sized child elements will collapse to the smallest size possible while maintaining their relative proportions and ensuring all child elements are fully visible.

> [!Sample WrapPanel2BasicSample]

## Properties

### Items Justification

The `ItemsJustification` property determines how items are aligned and distributed along a line.

#### Automatic

Arranges items according to the control's alignment.

#### Start / Center / End

Aligns items to the beginning, middle, or end of the line.

#### SpaceAround

Equal internal padding with half-sized padding at margins.

#### SpaceBetween

Equal spacing between items; no margin padding.

#### SpaceEvenly

Equal spacing between all items and margins.

### Items Stretch

The `ItemsStretch` property defines how the panel fills space on lines that do not contain Star-sized definitions, or when forced to fill a fixed row length.

#### None

No additional stretching is applied to non-star items. Note that Star-sized items will still expand if the `ItemsJustification` mode triggers a stretch.

#### First

The first item in the line is stretched to occupy all remaining space.

#### Last

The last item in the line is stretched to occupy all remaining space.

#### Equal

Every item in the line is stretched to a uniform size to fill the row, regardless of their individual content size.

#### Proportional

Every item in the line is stretched proportionally based on their desired size to fill the remaining space.

## Additional Samples

### Adjusted Sizings Sample

Demonstrates a mix of Auto, Pixel, and Star lengths within a wrapping layout.

> [!Sample WrapPanel2MegaSample]

### Proportional Sizing

Demonstrates how Star-sized items maintain ratios even when the panel is not set to stretch.

> [!Sample WrapPanel2ProportionalSample]

51 changes: 51 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2BasicSample.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!-- 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="WrapPanel2Experiment.Samples.WrapPanel2BasicSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:WrapPanel2Experiment.Samples"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Page.Resources>
<Style TargetType="Border">
<Setter Property="Background" Value="#55000000" />
<Setter Property="BorderBrush" Value="#88000000" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
</Style>
</Page.Resources>

<Grid MaxHeight="600"
Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>

<StackPanel Margin="8"
Orientation="Horizontal"
Spacing="8">
<Button Click="AddItemClick"
Content="Add Item" />
<Button Click="Add5ItemsClick"
Content="Add 5 Items" />
<Button Click="ClearItemsClick"
Content="Clear" />
</StackPanel>

<controls:WrapPanel2 x:Name="WrapPanel"
Grid.Row="1"
HorizontalAlignment="{x:Bind LayoutHorizontalAlignment, Mode=OneWay}"
VerticalAlignment="{x:Bind LayoutVerticalAlignment, Mode=OneWay}"
ItemSpacing="{x:Bind ItemSpacing, Mode=OneWay}"
ItemsJustification="{x:Bind local:WrapPanel2BasicSample.ConvertStringToItemsJustification(LayoutItemsJustification), Mode=OneWay}"
ItemsStretch="{x:Bind local:WrapPanel2BasicSample.ConvertStringToItemsStretch(LayoutItemsStretch), Mode=OneWay}"
LineSpacing="{x:Bind LineSpacing, Mode=OneWay}"
Orientation="{x:Bind local:WrapPanel2BasicSample.ConvertStringToOrientation(LayoutOrientation), Mode=OneWay}" />
</Grid>
</Page>
118 changes: 118 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2BasicSample.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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.

using CommunityToolkit.WinUI.Controls;

namespace WrapPanel2Experiment.Samples;

/// <summary>
/// An example sample page of a custom control inheriting from Panel.
/// </summary>
[ToolkitSampleMultiChoiceOption("LayoutOrientation", "Horizontal", "Vertical", Title = "Orientation")]
[ToolkitSampleMultiChoiceOption("LayoutHorizontalAlignment", "Left", "Center", "Right", "Stretch", Title = "Horizontal Alignment")]
[ToolkitSampleMultiChoiceOption("LayoutVerticalAlignment", "Top", "Center", "Bottom", "Stretch", Title = "Vertical Alignment")]
[ToolkitSampleNumericOption("ItemSpacing", 8, 0, 16, Title = "Item Spacing")]
[ToolkitSampleNumericOption("LineSpacing", 2, 0, 16, Title = "Line Spacing")]
[ToolkitSampleMultiChoiceOption("LayoutItemsJustification", "Automatic", "Start", "Center", "End", "SpaceAround", "SpaceBetween", "SpaceEvenly", Title = "Items Justification")]
[ToolkitSampleMultiChoiceOption("LayoutItemsStretch", "None", "First", "Last", "Equal", "Proportional", Title = "Items Stretch")]

[ToolkitSample(id: nameof(WrapPanel2BasicSample), $"Basic demo of the {nameof(WrapPanel2)} with auto-sized items.", description: $"A sample showing every property of the {nameof(WrapPanel2)} panel.")]
public sealed partial class WrapPanel2BasicSample : Page
{
public WrapPanel2BasicSample()
{
this.InitializeComponent();
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should start with some items in the collection by default, I was very confused about the properties not doing anything at first, not realizing the buttons were control buttons and not part of the collection.

We actually need to just make these action buttons consistent with a little docked status bar or something in the sample infrastructure, it's been on the backlog for a while, maybe I'll take a quick look into: CommunityToolkit/Tooling-Windows-Submodule#4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
}
Add5ItemsClick(null!, null!);
}

Should we just call the method for now in the constructor to have something?


// TODO: See https://github.com/CommunityToolkit/Labs-Windows/issues/149
public static Orientation ConvertStringToOrientation(string orientation) => orientation switch
{
"Vertical" => Orientation.Vertical,
"Horizontal" => Orientation.Horizontal,
_ => throw new System.NotImplementedException(),
};

// TODO: See https://github.com/CommunityToolkit/Labs-Windows/issues/149
public static HorizontalAlignment ConvertStringToHorizontalAlignment(string alignment) => alignment switch
{
"Left" => HorizontalAlignment.Left,
"Center" => HorizontalAlignment.Center,
"Right" => HorizontalAlignment.Right,
"Stretch" => HorizontalAlignment.Stretch,
_ => throw new System.NotImplementedException(),
};

// TODO: See https://github.com/CommunityToolkit/Labs-Windows/issues/149
public static VerticalAlignment ConvertStringToVerticalAlignment(string alignment) => alignment switch
{
"Top" => VerticalAlignment.Top,
"Center" => VerticalAlignment.Center,
"Bottom" => VerticalAlignment.Bottom,
"Stretch" => VerticalAlignment.Stretch,
_ => throw new System.NotImplementedException(),
};

// TODO: See https://github.com/CommunityToolkit/Labs-Windows/issues/149
public static WrapPanelItemsJustification ConvertStringToItemsJustification(string itemsJustification) => itemsJustification switch
{
"Automatic" => WrapPanelItemsJustification.Automatic,
"Start" => WrapPanelItemsJustification.Start,
"Center" => WrapPanelItemsJustification.Center,
"End" => WrapPanelItemsJustification.End,
"SpaceAround" => WrapPanelItemsJustification.SpaceAround,
"SpaceBetween" => WrapPanelItemsJustification.SpaceBetween,
"SpaceEvenly" => WrapPanelItemsJustification.SpaceEvenly,
_ => throw new System.NotImplementedException(),
};

// TODO: See https://github.com/CommunityToolkit/Labs-Windows/issues/149
public static WrapPanelItemsStretch ConvertStringToItemsStretch(string stretchMethod) => stretchMethod switch
{
"None" => WrapPanelItemsStretch.None,
"First" => WrapPanelItemsStretch.First,
"Last" => WrapPanelItemsStretch.Last,
"Equal" => WrapPanelItemsStretch.Equal,
"Proportional" => WrapPanelItemsStretch.Proportional,
_ => throw new System.NotImplementedException(),
};

private int _index;

private string[] LoremIpsumWords => LoremIpsum.Split(' ');

private string LoremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam fermentum placerat pretium. Phasellus molestie faucibus purus ut semper. Etiam felis ante, condimentum sed leo in, aliquam pharetra libero. Etiam ante ante, sagittis in semper eu, aliquam non sapien. Donec a pharetra magna. Suspendisse et nulla magna. Cras varius sem dolor, ac faucibus turpis malesuada ac. Maecenas rutrum tortor et faucibus rutrum. Vestibulum in gravida odio, non dapibus dui. Praesent leo tellus, vulputate sed sollicitudin id, fringilla quis ligula. Cras eget ex vitae purus pulvinar mattis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec consectetur tellus id augue ultrices, eget congue tellus pharetra.";

private void AddItemClick(object sender, RoutedEventArgs e)
{
AddItem();
}

private void Add5ItemsClick(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 5; i++)
AddItem();
}

private void ClearItemsClick(object sender, RoutedEventArgs e)
{
WrapPanel.Children.Clear();
_index = 0;
}

private void AddItem()
{
_index = _index % LoremIpsumWords.Length;

var currentWord = LoremIpsumWords[_index++];
var border = new Border()
{
Child = new TextBlock()
{
Text = currentWord,
}
};

WrapPanel.Children.Add(border);
}
}
Loading
Loading