diff --git a/specs/WCT/Primitives/WrapPanel.md b/specs/WCT/Primitives/WrapPanel.md new file mode 100644 index 0000000000..73afeb4dc6 --- /dev/null +++ b/specs/WCT/Primitives/WrapPanel.md @@ -0,0 +1,201 @@ + + + + +WrapPanel +=== + +# Background + +_This spec brings WrapPanel from the Windows Community Toolkit (as WPF Polyfill) to WinUI._ + +WrapPanel has been a staple of XAML development since its inception, originally included in WPF +[WrapPanel](https://learn.microsoft.com/dotnet/desktop/wpf/controls/wrappanel). It was originally added +to the Windows Community Toolkit (WCT) in v1.3 on 2/10/2017 +([see WCT history here](https://github.com/CommunityToolkit/Windows/discussions/722)) and used in a +multitude of applications since. + +`WrapPanel` is a simple and versatile panel that positions child controls vertically or horizontally +based on the orientation in sequence and when max width / max height is reached a new virtual row (in +case of horizontal) or column (in case of vertical) is automatically created to fit new controls. + +This differs importantly from `StackPanel` where all children must be in a single row/column and aids in +responsive UI flow. + +> [!IMPORTANT] +> About WPF Compatibility: This spec aligns to the WCT implementation for compatibility for existing +WinUI users. WrapPanel in the WCT was modernized and updated as part of general Panel modernization for +responsive layouts done for UWP and simplified for developer's ease-of-use. It is the goal to bring this +to WinUI for this same fundamental experience and allow easy migration from WCT developers and existing +usage in WinUI apps vs. direct migration for WPF developers. + +Reference Docs: +- [WCT WrapPanel Conceptual](https://learn.microsoft.com/dotnet/communitytoolkit/windows/primitives/wrappanel) +- [WCT WrapPanel API](https://learn.microsoft.com/dotnet/api/communitytoolkit.winui.controls.wrappanel) +- [WinUI StackPanel Conceptual](https://learn.microsoft.com/windows/apps/design/layout/layout-panels#stackpanel) +- [WinUI StackPanel API Comparison](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.stackpanel) +- [WPF WrapPanel Conceptual](https://learn.microsoft.com/dotnet/desktop/wpf/controls/wrappanel) +- [WPF WrapPanel API](https://learn.microsoft.com/dotnet/api/system.windows.controls.wrappanel) +- [WPF StackPanel API Comparison](https://learn.microsoft.com/dotnet/api/system.windows.controls.stackpanel) + + +# Conceptual pages (How To) + + + + + +The WrapPanel is a layout panel that arranges child elements in a sequential position from left to right, +items overflowing the line will break to the next line automatically at the edge of the containing panel. + +You can set the `Orientation` property to specify the direction of child elements. The default +orientation is **Horizontal**. + +The following XAML shows how to create a WrapPanel of items: + +```xml + + + + + + +``` + +The result looks like this: + +![WrapPanel example layout](images/layout-panel-wrap-panel.png) + +In a WrapPanel, if a child element's size is not set explicitly, it will be given the minimal amount +of space for layout. In this example, the size of the rectangles are explicitly set, if the +**Width** or **Height** were not provided, the rectangle would not appear (as it would be given a +**0** size in either dimension). This is a key contrast to a standard `StackPanel` control in terms +of default behavior. + +# API Pages + + + +## WrapPanel class + +The WrapPanel is a layout panel that arranges child elements in a sequential position from left to right, +items overflowing the line will break to the next line automatically at the edge of the containing panel. + +![WrapPanel example layout](images/WrapPanel.png) + +By default, WrapPanel stacks items from left to right in the order they are declared. You can set the +`Orientation` property to **Vertical** to stack items from top to bottom instead, overflowing to the +right. + +Spacing can be automatically added between items using the `HorizontalSpacing` and `VerticalSpacing` +properties. When the `Orientation` is **Horizontal**, `HorizontalSpacing` adds uniform horizontal spacing +between each individual item, and `VerticalSpacing` adds uniform spacing between each row of items. + +When the `Orientation` is **Vertical**, `HorizontalSpacing` adds uniform spacing between each column of +items, and `VerticalSpacing` adds uniform vertical spacing between individual items. + +## WrapPanel.HorizontalSpacing property + +Gets or sets a uniform Horizontal distance (in pixels) between items when Orientation is set to +Horizontal, or between columns of items when Orientation is set to Vertical. + +In the following example, HorizontalSpacing has been added to space out items, though in this case that +then wraps the Green box around to the next line due to the Width constraint on the WrapPanel: + +```xml + + + + + + +``` + +The result looks like this: + +![WrapPanel HorizontalSpacing example](images/WrapPanelHorizontalSpacing.png) + +## WrapPanel.Orientation property + +Gets or sets the orientation of the WrapPanel. **Horizontal** means that child controls will be added +horizontally until the width of the panel is reached, then a new row is added to add new child controls. +**Vertical** means that children will be added vertically until the height of the panel is reached, then +a new column is added. Default is **Horizontal**. + +## WrapPanel.Padding property + +Gets or sets the distance between the border and its child object. + +## WrapPanel.StretchChild property + +Gets or sets how the last child item in the WrapPanel is laid out. When set to **Last**, the final child +can stretch and be given all the rest of the available space for the row (when Orientation is +**Horizontal**) or column (when Orientation is **Vertical**). Defaults to **None** where no changes to +default behavior layout are provided. + +In the following example, the last Orange rectangle will be constrained and pushed to the 2nd row. +Normally if given the same Width/Height as the others, it'd sit just below the Red rectangle to the left. +Instead, we can remove it's Width and have it fill the rest of the row by setting `StretchChild` to **Last**: + +```xml + + + + + + +``` + +The result looks like this: + +![WrapPanel StretchChild example](images/WrapPanelStretchChild.png) + +## Other MyExample members + +| Name | Description | +|-|-| +| VerticalSpacing | Gets or sets a uniform Vertical distance (in pixels) between items when Orientation is set to Vertical, or between rows of items when Orientation is set to Horizontal. (defaults to 0) | + +# API Details + +```c# (but really MIDL3) +namespace Microsoft.UI.Xaml.Controls +{ + runtimeclass WrapPanel + { + double HorizontalSpacing { get; set; } + Orientation Orientation { get; set; } + Thickness Padding { get; set; } + StretchChild StretchChild { get; set; } + double VerticalSpacing { get; set; } + } + + enum StretchChild + { + None = 0, + Last + } +} +``` + +# Appendix + + diff --git a/specs/WCT/Primitives/images/WrapPanel.png b/specs/WCT/Primitives/images/WrapPanel.png new file mode 100644 index 0000000000..4367bc0f6b Binary files /dev/null and b/specs/WCT/Primitives/images/WrapPanel.png differ diff --git a/specs/WCT/Primitives/images/WrapPanelHorizontalSpacing.png b/specs/WCT/Primitives/images/WrapPanelHorizontalSpacing.png new file mode 100644 index 0000000000..9ab1dc595f Binary files /dev/null and b/specs/WCT/Primitives/images/WrapPanelHorizontalSpacing.png differ diff --git a/specs/WCT/Primitives/images/WrapPanelStretchChild.png b/specs/WCT/Primitives/images/WrapPanelStretchChild.png new file mode 100644 index 0000000000..258450b51a Binary files /dev/null and b/specs/WCT/Primitives/images/WrapPanelStretchChild.png differ diff --git a/specs/WCT/Primitives/images/layout-panel-wrap-panel.png b/specs/WCT/Primitives/images/layout-panel-wrap-panel.png new file mode 100644 index 0000000000..af529803b4 Binary files /dev/null and b/specs/WCT/Primitives/images/layout-panel-wrap-panel.png differ