From uno-platform
A responsive, non-virtualizing wrapping grid layout for ItemsRepeater that supports column spans, proportional stretch-to-fill, and vertically aligned gaps. Use when building dashboard-style or card-based layouts where items have varying widths but uniform height.
How this skill is triggered — by the user, by Claude, or both
Slash command
/uno-platform:uno-responsive-spanning-gridwrap-layoutThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A custom `NonVirtualizingLayout` for `ItemsRepeater` that arranges fixed-height items in a responsive wrapping grid with column span support.
A custom NonVirtualizingLayout for ItemsRepeater that arranges fixed-height items in a responsive wrapping grid with column span support.
Copy ResponsiveSpanningGridWrapLayout.cs into your project's controls folder and update the namespace.
MinColumnWidth| Property | Type | Default | Description |
|---|---|---|---|
MinColumnWidth | double | 200 | Minimum width per column; determines column count |
ColumnSpacing | double | 8 | Horizontal gap between items |
RowSpacing | double | 8 | Vertical gap between rows |
ItemHeight | double | 200 | Fixed height for all non-header items |
| Property | Type | Default | Description |
|---|---|---|---|
ColumnSpan | int | 1 | Number of columns an item spans. Values > maxColumns become full-width headers |
<ItemsRepeater ItemsSource="{x:Bind ViewModel.Items}">
<ItemsRepeater.Layout>
<local:ResponsiveSpanningGridWrapLayout
MinColumnWidth="250"
ColumnSpacing="12"
RowSpacing="12"
ItemHeight="180" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="models:MyItem">
<Border
local:ResponsiveSpanningGridWrapLayout.ColumnSpan="{x:Bind Span}"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="8"
Padding="16">
<!-- item content -->
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
Nested ItemsRepeater controls don't work on Uno Skia (inner repeater gets 0 width/height). For pages with different card types (metrics, charts, group headers), flatten all items into a single List<object> and use a DataTemplateSelector:
// ViewModel builds a flat interleaved list of different item types
public List<object> PageItems { get; } = [
new MetricViewModel { Title = "Power", Value = "342 kW", Span = 1 },
new MetricViewModel { Title = "Energy", Value = "1.2 MWh", Span = 1 },
new ChartViewModel { Title = "Power Flow", ChartType = "area" }, // span 2
new GroupHeaderViewModel { Title = "Devices" }, // full-width
new DeviceViewModel { Name = "Inverter 1" },
new DeviceViewModel { Name = "Battery 1" },
];
// DataTemplateSelector routes to the right template by item type
public class CardTemplateSelector : DataTemplateSelector
{
public DataTemplate? Metric { get; set; }
public DataTemplate? Chart { get; set; }
public DataTemplate? GroupHeader { get; set; }
public DataTemplate? Device { get; set; }
protected override DataTemplate? SelectTemplateCore(object item) => item switch
{
MetricViewModel => Metric,
ChartViewModel => Chart,
GroupHeaderViewModel => GroupHeader,
DeviceViewModel => Device,
_ => null,
};
}
<Page.Resources>
<DataTemplate x:Key="MetricTemplate">
<Border local:ResponsiveSpanningGridWrapLayout.ColumnSpan="{Binding Span}">
<ContentControl Style="{StaticResource AppCardContainerStyle}">
<cards:MetricCard/>
</ContentControl>
</Border>
</DataTemplate>
<DataTemplate x:Key="ChartTemplate">
<Border local:ResponsiveSpanningGridWrapLayout.ColumnSpan="2">
<ContentControl Style="{StaticResource AppCardContainerStyle}">
<cards:ChartCard/>
</ContentControl>
</Border>
</DataTemplate>
<!-- Full-width header: span >= maxColumns triggers auto-height -->
<DataTemplate x:Key="GroupHeaderTemplate">
<Border local:ResponsiveSpanningGridWrapLayout.ColumnSpan="99">
<TextBlock Text="{Binding Title}"
Style="{StaticResource SubtitleTextBlockStyle}"
Margin="0,8,0,0"/>
</Border>
</DataTemplate>
<DataTemplate x:Key="DeviceTemplate">
<ContentControl Style="{StaticResource AppCardContainerStyle}">
<cards:DeviceCard/>
</ContentControl>
</DataTemplate>
</Page.Resources>
<ItemsRepeater ItemsSource="{Binding PageItems}">
<ItemsRepeater.Layout>
<local:ResponsiveSpanningGridWrapLayout
MinColumnWidth="220" ColumnSpacing="12" RowSpacing="12" ItemHeight="180"/>
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<cards:CardTemplateSelector
Metric="{StaticResource MetricTemplate}"
Chart="{StaticResource ChartTemplate}"
GroupHeader="{StaticResource GroupHeaderTemplate}"
Device="{StaticResource DeviceTemplate}"/>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
ColumnSpan must go on the direct child of ItemsRepeater (typically the outer Border). ItemsRepeater only reads attached properties on direct children — putting it on an inner element has no effect.ColumnSpan >= maxColumns become full-width and get auto-height measurement instead of the fixed ItemHeight. Use a large value like 99 for group headers.uno-fluent2 skill §9) for consistent elevation, border, corner radius, and padding.When using ContentControl inside templates, do NOT set DataContext on the ContentControl itself if it also has a Visibility binding:
<!-- WRONG: Visibility binds to Child.IsVisible (wrong scope) -->
<ContentControl DataContext="{Binding Child}" Visibility="{Binding IsVisible}">
<!-- CORRECT: Set DataContext on the UserControl inside -->
<ContentControl Visibility="{Binding IsVisible}">
<cards:MyCard DataContext="{Binding Child}"/>
</ContentControl>
WHY: DataContext changes the binding scope BEFORE Visibility evaluates. The Visibility property looks up IsVisible on Child (wrong object) instead of the parent item, causing ghost content from silently failed bindings that default to Visible.
ScrollViewer — the layout needs a finite available width to calculate columnsItemHeightProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub vincenth-net/dotnet-agentic-engineering --plugin uno-platform