Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 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
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>
15 changes: 15 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2.Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<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>
</Project>
54 changes: 54 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
title: WrapPannel2
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 experiment for a new WrapPanel API using GridLength definitions to define the item's desired sizings.

When stretched along the main axis, the child elements with star-sized GridLength values will proportionally occupy the available space.

When not stretched along the main axis, star-sized child elements will be the smallest size possible while maintaining proportional sizing relative to each other and ensuring that all child elements are fully visible.


> [!Sample WrapPanel2BasicSample]

## Properties

### Fixed Row Length

When `FixedRowLengths` is enabled, all rows/columns will to stretch to the size of the largest row/column in the panel. When this is not enabled, rows/columns will size to their content individually.

### Forced Stretch Method

The `ForcedStretchMethod` property allows you to specify how the panel should handle stretching in rows without star-sized definitions.

#### None

When set to `None`, this panel will not stretch rows/columns that do not have star-sized definitions. When the alignment is set to stretch, and even when fixed row lengths is enabled, the rows/columns without star-sized definitions will size to their content.

#### First

When set the `First`, this panel will stretch the first item in the row/column to occupy the remaining space when needed to comply with stretch alignment.

#### Last

When set to `Last`, this panel will stretch the last item in the row/column to occupy the remaining space when needed to comply with stretch alignment.

#### Equal

When set to `Equal`, this panel will stretch all items in the row/column to occupy the equal space throughout the row when needed to comply with stretch alignment.

#### Proportional

When set to `Proportional`, this panel will stretch all items in the row/column proportionally to their defined size to occupy the remaining space when needed to comply with stretch alignment.
83 changes: 83 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2BasicSample.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!-- 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">
<controls:WrapPanel2 HorizontalAlignment="{x:Bind LayoutHorizontalAlignment, Mode=OneWay}"
VerticalAlignment="{x:Bind LayoutVerticalAlignment, Mode=OneWay}"
FixedRowLengths="{x:Bind FixedRowLengths, Mode=OneWay}"
ItemSpacing="{x:Bind ItemSpacing, Mode=OneWay}"
LineSpacing="{x:Bind LineSpacing, Mode=OneWay}"
Orientation="{x:Bind local:WrapPanel2BasicSample.ConvertStringToOrientation(LayoutOrientation), Mode=OneWay}"
OverflowBehavior="{x:Bind local:WrapPanel2BasicSample.ConvertStringToOverflowBehavior(LayoutOverflowBehavior), Mode=OneWay}"
StretchChildren="{x:Bind local:WrapPanel2BasicSample.ConvertStringToForcedStretchMethod(LayoutStretchChildren), Mode=OneWay}">
<Border controls:WrapPanel2.LayoutLength="2*">
<TextBlock HorizontalAlignment="Center"
Text="2*" />
</Border>
<Border controls:WrapPanel2.LayoutLength="Auto">
<TextBlock HorizontalAlignment="Center"
Text="Auto" />
</Border>
<Border controls:WrapPanel2.LayoutLength="400">
<TextBlock HorizontalAlignment="Center"
Text="400px" />
</Border>
<Border controls:WrapPanel2.LayoutLength="1*">
<TextBlock HorizontalAlignment="Center"
Text="1*" />
</Border>
<Border controls:WrapPanel2.LayoutLength="200">
<TextBlock HorizontalAlignment="Center"
Text="200px" />
</Border>
<Border controls:WrapPanel2.LayoutLength="300">
<TextBlock HorizontalAlignment="Center"
Text="300px" />
</Border>
<Border controls:WrapPanel2.LayoutLength="600">
<TextBlock HorizontalAlignment="Center"
Text="600px" />
</Border>
<Border controls:WrapPanel2.LayoutLength="2*">
<TextBlock HorizontalAlignment="Center"
Text="2*" />
</Border>
<Border controls:WrapPanel2.LayoutLength="1.5*">
<TextBlock HorizontalAlignment="Center"
Text="1.5*" />
</Border>
<Border controls:WrapPanel2.LayoutLength="Auto">
<TextBlock HorizontalAlignment="Center"
Text="Auto with longer text" />
</Border>
<Border controls:WrapPanel2.LayoutLength="400">
<TextBlock HorizontalAlignment="Center"
Text="400px" />
</Border>
<Border controls:WrapPanel2.LayoutLength="1*">
<TextBlock HorizontalAlignment="Center"
Text="1*" />
</Border>
</controls:WrapPanel2>
</Grid>
</Page>
75 changes: 75 additions & 0 deletions components/WrapPanel2/samples/WrapPanel2BasicSample.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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")]
[ToolkitSampleBoolOption("FixedRowLengths", false, Title = "Fixed Row Lengths")]
[ToolkitSampleMultiChoiceOption("LayoutStretchChildren", "StarSizedOnly", "First", "Last", "Equal", "Proportional", Title = "Forced Stretch Method")]
[ToolkitSampleMultiChoiceOption("LayoutOverflowBehavior", "Wrap", "Drop", Title = "Overflow Behavior")]

[ToolkitSample(id: nameof(WrapPanel2BasicSample), "WrapPanel2 Basic Sample", description: $"A sample for showing how to use a {nameof(WrapPanel2)} panel.")]
public sealed partial class WrapPanel2BasicSample : Page
{
public WrapPanel2BasicSample()
{
this.InitializeComponent();
}

// 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 StretchChildren ConvertStringToForcedStretchMethod(string stretchMethod) => stretchMethod switch
{
"StarSizedOnly" => StretchChildren.StarSizedOnly,
"First" => StretchChildren.First,
"Last" => StretchChildren.Last,
"Equal" => StretchChildren.Equal,
"Proportional" => StretchChildren.Proportional,
_ => throw new System.NotImplementedException(),
};

// TODO: See https://github.com/CommunityToolkit/Labs-Windows/issues/149
public static OverflowBehavior ConvertStringToOverflowBehavior(string overflowBehavior) => overflowBehavior switch
{
"Wrap" => OverflowBehavior.Wrap,
"Drop" => OverflowBehavior.Drop,
_ => throw new System.NotImplementedException(),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />

<PropertyGroup>
<ToolkitComponentName>WrapPanel2</ToolkitComponentName>
<Description>This package contains WrapPanel2.</Description>

<!-- Rns suffix is required for namespaces shared across projects. See https://github.com/CommunityToolkit/Labs-Windows/issues/152 -->
<RootNamespace>CommunityToolkit.WinUI.Controls.WrapPanel2Rns</RootNamespace>
</PropertyGroup>

<!-- Sets this up as a toolkit component's source project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SourceProject.props" />
</Project>
31 changes: 31 additions & 0 deletions components/WrapPanel2/src/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>
9 changes: 9 additions & 0 deletions components/WrapPanel2/src/MultiTarget.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<!--
MultiTarget is a custom property that indicates which target a project is designed to be built for / run on.
Used to create project references, generate solution files, enable/disable TargetFrameworks, and build nuget packages.
-->
<MultiTarget>uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android;</MultiTarget>
</PropertyGroup>
</Project>
21 changes: 21 additions & 0 deletions components/WrapPanel2/src/OverflowBehavior.cs
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>
/// Describes the behavior of items that exceed the available space in the panel.
/// </summary>
public enum OverflowBehavior
{
/// <summary>
/// When an item exceeds the available space, it will be moved to a new row or column.
/// </summary>
Wrap,

/// <summary>
/// Items which do not fit within the available space will be removed from the layout.
/// </summary>
Drop,
}
36 changes: 36 additions & 0 deletions components/WrapPanel2/src/StretchChildren.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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>
/// Describes the behavior for stretching childrenn in the wrap-panel.
/// </summary>
public enum StretchChildren
{
/// <summary>
/// Only star-sized items will ever be stretched.
/// </summary>
StarSizedOnly,

/// <summary>
/// In a row without star-sized items, the first item in the row will be stretched to fill the row.
/// </summary>
First,

/// <summary>
/// In a row without star-sized items, the last item in the row will be stretched to fill the row.
/// </summary>
Last,

/// <summary>
/// In a row without star-sized items, each item will be stretched to an equal size to fill the row.
/// </summary>
Equal,

/// <summary>
/// In a row without star-sized items, each item will be stretched proportional to their desired size to fill the row.
/// </summary>
Proportional,
}
Loading
Loading