name: uno-xaml description: "Uno Platform XAML correctness and performance guidance for XAML markup profiles: deferred loading with x:Load, virtualized item-template mechanics, text trimming and layout resilience, UI-bound lifecycle cleanup, UI-thread safety at the Page/control boundary, input scopes, keyboard accelerators, focus, and drag/drop caveats. Use when the selected markup type is XAML or when existing XAML needs performance, lifecycle, input, text overflow, or template fixes. Do NOT use for MVVM property/command patterns, Uno Navigation, Fluent or Material theming, visual design/layout composition, Uno C# Markup, or C# Markup 2; combine with the selected update-model, design-system, navigation, and markup skills instead." metadata: author: https://github.com/VincentH-Net version: "1.1" framework: uno-platform category: xaml sources: - https://github.com/mtmattei/UnoPlatformSkills/tree/main/winui-xaml - Microsoft Learn WinUI XAML guidance - Uno Platform compatibility caveats
Uno Platform XAML
Use this skill for XAML-only correctness and performance details that are not owned by the selected update model, design system, navigation, or layout skill.
Composition Rules
- If the user selected C# Markup or C# Markup 2 for a view, do not apply this skill to that view's markup syntax.
- For MVVM bindings, generated properties, commands, and ViewModel structure, use
uno-mvvm. - For MVUX feeds, states, records, commands, and generated ViewModels, use the selected MVUX skills.
- For Uno Extensions Navigation, do not introduce
Frame.Navigate()patterns. UseINavigator, route maps,Region.*, andNavigation.*guidance from the selected navigation/update-model skill. - For Fluent or Material resource keys, theme dictionaries, typography, colors, shadows, and lightweight styling, use the selected design-system skill. Do not copy generic WinUI theming snippets into a themed Uno app.
- For responsive visual composition, spacing, margins, accessibility naming, and localization naming, follow the Uno MCP platform usage rules and the selected design-system skill.
Binding Boundaries
- Prefer
x:Bindonly where the source is strongly typed and stable, such as a Page-levelViewModelproperty or aDataTemplatewith a validx:DataType. - Use ordinary
{Binding}where Uno template scope requires it, such as nested template item sources, ancestor bindings, or command bindings that need a controlDataContext. - Do not use
{Binding StringFormat=...}. Use multipleRunelements or expose a computed property from the selected update model. - Avoid converter stacks for view state that belongs in the ViewModel/model. Use a converter only for view-only type conversion that cannot reasonably live in the update model.
Deferred Loading
Use x:Load for expensive optional XAML that is not needed at startup, such as advanced settings panels, secondary tab content, help overlays, or infrequently opened details regions.
<Grid x:Name="AdvancedPanel"
x:Load="{x:Bind ViewModel.ShowAdvancedPanel, Mode=OneWay}">
<!-- Expensive optional content -->
</Grid>
Rules:
- Prefer
Visibilityfor cheap, frequently toggled state such as validation messages, simple placeholders, and selected visual states. - Prefer
x:Loadwhen delaying construction is the goal; unloaded content releases the element tree and its state. x:Loadrequires a named element and cannot be used on the root element or inside aResourceDictionary.- Be careful with
ElementNamebindings to unloaded elements. If another element binds to a deferred element, load order can break the binding. - Do not use legacy
x:DeferLoadStrategy; usex:Load. - Verify
x:Loadbehavior on all target Uno platforms when the loaded subtree contains platform-specific controls, focus targets, popups, WebView, media, or third-party controls.
Virtualized Templates
For large linear lists, use virtualized controls such as ListView or GridView. For custom dashboard grids, wrapping cards, or mixed-width tiles, use the existing responsive ItemsRepeater skill instead of generic WinUI list patterns.
Rules:
- Do not wrap a virtualized
ListVieworGridViewin aScrollViewer; it disables or harms virtualization. - Give virtualized items stable dimensions where practical. Variable-height templates make measuring expensive and scrolling less predictable.
- Keep item templates shallow. Move repeated chrome into shared styles/templates and avoid nested panels that do not add layout value.
- Avoid
x:Nameon elements inside virtualizedDataTemplates unless there is no alternative. Named template elements are per-container and easy to misuse. - Prefer binding and commands inside templates. Use code-behind template events only for view-only mechanics that cannot be expressed as a command or behavior.
- Use
x:Phaseonly for complexListView/GridViewitem templates after confirming support on the target Uno platforms. Do not apply it toItemsRepeaterdashboard layouts by default. - Use
DataTemplateSelectorfor heterogeneous item types, but keep selector logic type-based and cheap. Do not perform service calls, visual tree inspection, or layout measurements in selector logic.
Text and Layout Resilience
Keep text containers flexible so localization, text scaling, and runtime data do not clip or overlap.
Rules:
- Prefer
MinHeightandMinWidthover fixedHeightandWidthfor controls that contain text. - Avoid fixed heights on text containers. Let
TextBlock,TextBox, and item rows grow when text scale or localization requires it. - Avoid fixed button widths unless repeated command columns need alignment. Use content-driven sizing with a sensible
MinWidth. - Use
Gridconstraints whenTextTrimmingmust work.StackPanelandAutocolumns often measure children with unconstrained width, so ellipsis may never appear. - Use star-sized columns for trimmable text and
Autocolumns for fixed-size icons, badges, or actions. - Keep headers, command bars, and primary actions outside the scrolling region when the page has long content. Only the content area should scroll unless the whole page is intentionally document-like.
- Use
VerticalScrollBarVisibility="Auto"when aScrollVieweris required. - Set
HorizontalContentAlignment="Stretch"on list-like item containers when content collapses or fails to fill the available width. - Remove wrapper
Grid,StackPanel, orBorderelements that do not add layout, styling, semantics, or hit testing. - Use
Borderfor a single-child background, border, or corner-radius container; useGridwhen you need rows, columns, layering, or multiple children.
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"
TextTrimming="CharacterEllipsis"/>
<Button Grid.Column="1"
Command="{Binding OpenCommand}"
MinWidth="44"/>
</Grid>
UI Thread and Async Safety
XAML views and controls are UI-thread-bound. Keep async work non-blocking and keep dispatcher usage at the view/control boundary.
Rules:
- Never block the UI thread with
.Result,.Wait(),Thread.Sleep, or synchronous I/O. - Use
awaitall the way through UI-triggered work. - Use
Task.WhenAllfor independent asynchronous operations; sequence only when one result depends on another. - Use
DispatcherQueue.TryEnqueueonly when a background callback must update a UI element directly. - Do not put
DispatcherQueue,Page,Button,TextBox, or visual tree access in ViewModels. Route state through commands, services, or observable properties instead. - In non-UI library code,
ConfigureAwait(false)is acceptable. Do not use it in UI code before touching XAML controls. - Avoid
async voidexcept for required UI event handlers. In those handlers, catch/report failures and delegate real work to commands or services.
UI Lifecycle Cleanup
Use code-behind only for UI-only lifecycle mechanics, platform interop, or control events that cannot be represented cleanly as commands.
Rules:
- Unsubscribe UI event handlers in
Unloadedwhen the publisher can outlive the view. - Stop and detach
DispatcherQueueTimerinstances inUnloaded. - Cancel in-flight UI-owned operations when the view unloads.
- Dispose UI-owned
IDisposableresources such as streams, subscriptions, and temporary media objects. - Avoid lambdas that capture
thisfor long-lived events. Prefer named handlers that can be removed, or weak subscriptions when the publisher is application-scoped. - Page cleanup must not dispose shared services from dependency injection; only clean resources owned by the view instance.
public sealed partial class DetailsPage : Page
{
CancellationTokenSource? loadCts;
public DetailsPage()
{
InitializeComponent();
Unloaded += OnUnloaded;
}
void OnUnloaded(object sender, RoutedEventArgs e)
{
Unloaded -= OnUnloaded;
loadCts?.Cancel();
loadCts?.Dispose();
loadCts = null;
}
}
Input Affordances
Add input details that improve correctness without changing the selected design system.
Rules:
- Set
InputScopeon text inputs where the expected value is known.
<TextBox Text="{x:Bind ViewModel.Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
InputScope="EmailSmtpAddress" />
<TextBox Text="{x:Bind ViewModel.Phone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
InputScope="TelephoneNumber" />
<TextBox Text="{x:Bind ViewModel.Quantity, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
InputScope="Number" />
- Use
KeyboardAcceleratorfor common page or menu actions on desktop-class experiences. Route the invoked handler to a command or service; keep the handler thin. - Preserve focus order. When deferred content loads, explicitly set focus only when it matches the user's workflow, such as opening a search panel or dialog.
- Use pointer events for custom pointer-specific UI only. Prefer built-in controls, commands, and gestures before custom pointer handling.
- Drag/drop usually requires UI events. Keep drag/drop code in the view boundary, pass IDs or small DTOs, and call commands/services for the actual mutation. Test drag/drop on every target platform before relying on it.
Accessibility and Test Hooks
- Set
AutomationProperties.Nameon icon-only controls and custom interactive elements whose visible content is not enough for assistive technology. - Use semantic controls such as
Button,HyperlinkButton,ToggleSwitch, andMenuFlyoutIteminstead of makingBorder,Grid, orTextBlockclickable. - Use
AutomationProperties.AutomationIdfor elements that UI tests must find reliably. On Uno, native/browser automation mapping requiresIsUiAutomationMappingEnabledorUno.UI.FrameworkElementHelper.IsUiAutomationMappingEnabled; otherwise the property may not be projected to the platform accessibility layer. - Keep light-dismiss and overlay hit targets hit-test visible. If an invisible surface must receive pointer input, use
Background="Transparent"rather than leaving the background unset.
Rendering And Visibility
- Use
Visibility="Collapsed"instead ofOpacity="0"when hidden content should not participate in hit testing, accessibility, or layout. - Use
Opacityonly for visual fades where the element should remain present during the animation. - Use
x:Loadrather thanVisibilitywhen expensive optional UI should not be created until needed. - Do not add blur, Acrylic, Mica, custom shadows, or theme resource guidance from generic WinUI examples. Follow the selected design-system skill and Uno platform compatibility notes.
Review Checklist
- XAML view uses the selected update model's binding and command pattern.
- XAML view does not introduce
Frame.Navigate()in an Uno Extensions Navigation app. - Theme dictionaries and resource keys come only from the selected design-system skill.
- Deferred UI uses
x:Loadonly where construction cost justifies it. - Virtualized controls are not wrapped in
ScrollViewer. - Text containers avoid fixed heights; trimmable text is constrained by a
Gridor equivalent width constraint. - Long/localized strings and text scaling do not clip content or resize command surfaces unpredictably.
- Template code avoids unnecessary
x:Name, deep trees, and expensive selector logic. - UI-owned events, timers, and cancellation tokens are cleaned up on unload.
- UI thread is never blocked.
- Text inputs have appropriate
InputScope. - Icon-only controls and UI-test targets have appropriate automation properties.
- Keyboard, focus, pointer, and drag/drop logic remains at the view boundary and delegates business state changes.