Skip to main content

Layout Management

Avalonia Layout Management

Effective layout management is crucial for creating well-organized, responsive, and maintainable user interfaces. Avalonia UI provides a comprehensive set of layout panels and containers that give you precise control over how elements are arranged and how they respond to changes in available space.

Layout System Overview

Avalonia's layout system follows a two-pass process:

  1. Measure Pass: The parent container asks each child how much space it needs
  2. Arrange Pass: The parent container positions each child based on available space

This process happens automatically whenever:

  • The UI is first rendered
  • A property changes that affects layout (e.g., Width, Height, Margin)
  • The window is resized
  • Content is added or removed

Panels and Layout Containers

Avalonia UI offers several specialized layout panels, each designed for specific layout scenarios:

Panel

Panel is the base class for all layout containers in Avalonia UI. It provides the fundamental infrastructure for arranging child elements:

<Panel Background="#f0f0f0" Margin="10">
<TextBlock Text="This is a panel" Foreground="#333" />
<Button Content="Button in panel"
Background="#1E88E5"
Foreground="White"
Padding="10,5" />
</Panel>

Behavior:

  • By default, Panel positions all children at the same location (0,0)
  • Children are drawn in the order they're declared (later children appear on top)
  • No automatic sizing or positioning logic

When to use:

  • As a base for custom panels
  • When you need a simple container with overlapping elements
  • For creating layered UIs where elements stack on top of each other

Key properties:

  • Background: Sets the panel's background
  • Children: Collection of child elements

Canvas

Canvas provides absolute positioning capabilities, allowing you to place elements at precise coordinates:

<Canvas Width="300" Height="200" Background="#f5f5f5">
<!-- Rectangle with drop shadow effect -->
<Rectangle Canvas.Left="10" Canvas.Top="10"
Width="100" Height="100"
Fill="#E91E63" Opacity="0.9">
<Rectangle.Effect>
<DropShadowEffect OffsetX="3" OffsetY="3" BlurRadius="5" />
</Rectangle.Effect>
</Rectangle>

<!-- Ellipse with gradient -->
<Ellipse Canvas.Left="100" Canvas.Top="100"
Width="150" Height="100">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="#2196F3" />
<GradientStop Offset="1" Color="#4CAF50" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>

<!-- Text with absolute positioning -->
<TextBlock Canvas.Left="50" Canvas.Top="50"
Text="Absolute positioning"
FontWeight="Bold"
Foreground="White">
<TextBlock.Effect>
<DropShadowEffect Color="Black" />
</TextBlock.Effect>
</TextBlock>
</Canvas>

Behavior:

  • Children are positioned using explicit coordinates
  • No automatic layout adjustments
  • Elements can be positioned partially or completely outside the canvas
  • Z-order is determined by the order of declaration

When to use:

  • For drawing or diagramming applications
  • When you need precise control over element positions
  • For custom visualizations or animations
  • When implementing drag-and-drop interfaces

Key properties:

  • Canvas.Left: Distance from the left edge (attached property)
  • Canvas.Top: Distance from the top edge (attached property)
  • Canvas.Right: Distance from the right edge (attached property)
  • Canvas.Bottom: Distance from the bottom edge (attached property)
  • Canvas.ZIndex: Controls the stacking order (higher values appear on top)

StackPanel

StackPanel arranges elements in a single line, either vertically (default) or horizontally, with precise control over spacing and alignment:

<!-- Vertical stack with styling and spacing -->
<StackPanel Background="#f9f9f9"
Margin="10"
Padding="15"
Spacing="8"
Width="250"
HorizontalAlignment="Center"
CornerRadius="4">
<!-- Header -->
<TextBlock Text="Settings Menu"
FontWeight="Bold"
FontSize="18"
Margin="0,0,0,10"
HorizontalAlignment="Center" />

<!-- Menu items with consistent styling -->
<Button Content="Profile Settings"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="10,8"
Background="#E3F2FD"
Foreground="#1565C0" />

<Button Content="Account Security"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="10,8"
Background="#E8F5E9"
Foreground="#2E7D32" />

<Button Content="Notification Preferences"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="10,8"
Background="#FFF3E0"
Foreground="#E65100" />

<!-- Footer with different alignment -->
<Button Content="Sign Out"
Margin="0,10,0,0"
Padding="15,8"
Background="#FFEBEE"
Foreground="#C62828"
HorizontalAlignment="Right" />
</StackPanel>
<!-- Horizontal stack with varied content -->
<StackPanel Orientation="Horizontal"
Spacing="12"
Margin="10"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<!-- Icon -->
<Ellipse Width="48" Height="48" Fill="#1E88E5">
<Ellipse.Effect>
<DropShadowEffect BlurRadius="4" OffsetX="2" OffsetY="2" />
</Ellipse.Effect>
</Ellipse>

<!-- Text content -->
<StackPanel Spacing="4" VerticalAlignment="Center">
<TextBlock Text="John Doe" FontWeight="SemiBold" FontSize="16" />
<TextBlock Text="Software Developer" Foreground="#757575" />
</StackPanel>

<!-- Action buttons -->
<StackPanel Orientation="Horizontal"
Spacing="8"
VerticalAlignment="Center"
Margin="20,0,0,0">
<Button Content="Message"
Padding="12,6"
Background="#4CAF50"
Foreground="White" />
<Button Content="Profile"
Padding="12,6"
Background="Transparent"
BorderBrush="#BDBDBD"
BorderThickness="1" />
</StackPanel>
</StackPanel>

Behavior:

  • Arranges children in a single line (vertical or horizontal)
  • Allocates as much space as children request in the stacking direction
  • In the other direction, children can be aligned or stretched
  • Does not constrain children to its own size
  • Does not wrap to a new line when space is exhausted

When to use:

  • For menus, toolbars, and simple forms
  • When you need a linear arrangement of elements
  • For creating headers, footers, or sidebars
  • When nesting layout containers to create more complex layouts

Key properties:

  • Orientation: Vertical (default) or Horizontal
  • Spacing: Gap between elements (more convenient than setting margins on each child)
  • HorizontalAlignment/VerticalAlignment: Control how the panel aligns within its parent
  • StackPanel.HorizontalAlignment/StackPanel.VerticalAlignment: Control how children align within the panel

Limitations:

  • Does not wrap content when it exceeds available space
  • Can cause scrolling issues if used as the root panel in a ScrollViewer
  • May not be suitable for responsive layouts without additional logic

WrapPanel

WrapPanel arranges elements in rows or columns and automatically wraps to a new line when there's not enough space, making it ideal for responsive layouts:

<!-- Responsive tag/chip layout -->
<Border Background="#f5f5f5" Padding="15" CornerRadius="4">
<WrapPanel Orientation="Horizontal" Margin="0" Spacing="8">
<!-- Tags/Chips with consistent styling but varied content -->
<Border Background="#E3F2FD" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#2196F3" VerticalAlignment="Center" />
<TextBlock Text="JavaScript" VerticalAlignment="Center" />
</StackPanel>
</Border>

<Border Background="#E8F5E9" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#4CAF50" VerticalAlignment="Center" />
<TextBlock Text="React" VerticalAlignment="Center" />
</StackPanel>
</Border>

<Border Background="#FFF8E1" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#FFC107" VerticalAlignment="Center" />
<TextBlock Text="TypeScript" VerticalAlignment="Center" />
</StackPanel>
</Border>

<Border Background="#FFEBEE" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#F44336" VerticalAlignment="Center" />
<TextBlock Text="Node.js" VerticalAlignment="Center" />
</StackPanel>
</Border>

<Border Background="#E0F7FA" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#00BCD4" VerticalAlignment="Center" />
<TextBlock Text="GraphQL" VerticalAlignment="Center" />
</StackPanel>
</Border>

<Border Background="#F3E5F5" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#9C27B0" VerticalAlignment="Center" />
<TextBlock Text="Docker" VerticalAlignment="Center" />
</StackPanel>
</Border>

<Border Background="#E8EAF6" CornerRadius="16" Padding="12,6">
<StackPanel Orientation="Horizontal" Spacing="6">
<Ellipse Width="8" Height="8" Fill="#3F51B5" VerticalAlignment="Center" />
<TextBlock Text="Kubernetes" VerticalAlignment="Center" />
</StackPanel>
</Border>
</WrapPanel>
</Border>
<!-- Image gallery with consistent sizing -->
<WrapPanel Orientation="Horizontal"
ItemWidth="150"
ItemHeight="150"
Margin="10"
Spacing="10">
<!-- Each image is forced to the same size by ItemWidth/ItemHeight -->
<Border Background="#E0E0E0" CornerRadius="4">
<Image Source="/Assets/Images/image1.jpg" Stretch="UniformToFill" />
</Border>
<Border Background="#E0E0E0" CornerRadius="4">
<Image Source="/Assets/Images/image2.jpg" Stretch="UniformToFill" />
</Border>
<Border Background="#E0E0E0" CornerRadius="4">
<Image Source="/Assets/Images/image3.jpg" Stretch="UniformToFill" />
</Border>
<Border Background="#E0E0E0" CornerRadius="4">
<Image Source="/Assets/Images/image4.jpg" Stretch="UniformToFill" />
</Border>
<Border Background="#E0E0E0" CornerRadius="4">
<Image Source="/Assets/Images/image5.jpg" Stretch="UniformToFill" />
</Border>
<Border Background="#E0E0E0" CornerRadius="4">
<Image Source="/Assets/Images/image6.jpg" Stretch="UniformToFill" />
</Border>
</WrapPanel>

Behavior:

  • Arranges children in rows or columns
  • Automatically wraps to a new line when there's not enough space
  • Can enforce uniform sizing for all children
  • Respects child margins when calculating layout

When to use:

  • For responsive layouts that need to adapt to different screen sizes
  • For galleries, tag clouds, or toolbars that might need to wrap
  • When displaying collections of similarly sized items
  • For creating flexible grid-like layouts without fixed columns

Key properties:

  • Orientation: Horizontal (default) or Vertical
  • ItemWidth/ItemHeight: Optional fixed size for all items
  • Spacing: Gap between elements (both horizontally and vertically)
  • HorizontalAlignment/VerticalAlignment: Control alignment within parent

Advantages over StackPanel:

  • Automatically wraps content when it exceeds available space
  • Better suited for responsive layouts
  • Can create grid-like layouts with items of equal size

DockPanel

DockPanel is a powerful layout container that allows you to dock elements to the edges of the container, creating sophisticated layouts with minimal code:

<!-- Application layout with header, footer, navigation, and content area -->
<DockPanel LastChildFill="True" Background="#f0f0f0">
<!-- Header area -->
<Border DockPanel.Dock="Top"
Height="60"
Background="#1976D2"
Padding="16,0">
<Grid ColumnDefinitions="Auto,*,Auto">
<!-- Logo/App name -->
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
Spacing="12">
<Ellipse Width="32" Height="32" Fill="White" />
<TextBlock Text="Avalonia App"
Foreground="White"
FontSize="20"
FontWeight="SemiBold"
VerticalAlignment="Center" />
</StackPanel>

<!-- Search box -->
<TextBox Grid.Column="1"
Width="300"
Watermark="Search..."
VerticalAlignment="Center"
Margin="20,0" />

<!-- User profile -->
<Button Grid.Column="2"
Content="John Doe"
Background="Transparent"
Foreground="White"
Padding="12,8"
VerticalAlignment="Center" />
</Grid>
</Border>

<!-- Footer area -->
<Border DockPanel.Dock="Bottom"
Height="40"
Background="#E0E0E0"
Padding="16,0">
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
Spacing="24">
<TextBlock Text="© 2023 Avalonia UI" Foreground="#757575" />
<TextBlock Text="Privacy Policy" Foreground="#1976D2" />
<TextBlock Text="Terms of Service" Foreground="#1976D2" />
<TextBlock Text="Contact" Foreground="#1976D2" />
</StackPanel>
</Border>

<!-- Navigation sidebar -->
<Border DockPanel.Dock="Left"
Width="220"
Background="White"
BoxShadow="2 0 10 0 #20000000">
<StackPanel Margin="0,20,0,0" Spacing="4">
<Button Content="Dashboard"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="20,10"
Background="#E3F2FD"
Foreground="#1565C0" />

<Button Content="Projects"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="20,10" />

<Button Content="Tasks"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="20,10" />

<Button Content="Calendar"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="20,10" />

<Button Content="Reports"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="20,10" />

<Button Content="Settings"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="20,10"
Margin="0,20,0,0" />
</StackPanel>
</Border>

<!-- Main content area (fills remaining space) -->
<Border Padding="20" Background="White">
<TextBlock Text="Main Content Area"
FontSize="24"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DockPanel>
<!-- Email client layout with toolbars and panels -->
<DockPanel>
<!-- Top toolbar -->
<StackPanel DockPanel.Dock="Top"
Orientation="Horizontal"
Spacing="8"
Margin="8"
Height="40">
<Button Content="New" Padding="12,6" />
<Button Content="Reply" Padding="12,6" />
<Button Content="Forward" Padding="12,6" />
<Button Content="Delete" Padding="12,6" />
<Button Content="Archive" Padding="12,6" />
</StackPanel>

<!-- Status bar -->
<Border DockPanel.Dock="Bottom"
Height="24"
Background="#f5f5f5"
Padding="8,0">
<TextBlock Text="5 messages selected"
VerticalAlignment="Center"
FontSize="12" />
</Border>

<!-- Folder list -->
<Border DockPanel.Dock="Left"
Width="180"
BorderBrush="#e0e0e0"
BorderThickness="0,0,1,0">
<StackPanel Margin="8">
<TextBlock Text="FOLDERS"
Margin="8,8,8,12"
FontSize="12"
Foreground="#757575" />
<Button Content="Inbox (12)"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="8,6"
Background="#E3F2FD" />
<Button Content="Sent"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="8,6" />
<Button Content="Drafts (3)"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="8,6" />
<Button Content="Spam"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="8,6" />
<Button Content="Trash"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="8,6" />
</StackPanel>
</Border>

<!-- Message preview pane -->
<Border DockPanel.Dock="Right"
Width="300"
BorderBrush="#e0e0e0"
BorderThickness="1,0,0,0"
Padding="12">
<StackPanel>
<TextBlock Text="Message Preview"
FontWeight="Bold"
FontSize="16"
Margin="0,0,0,12" />
<TextBlock Text="From: john.doe@example.com" Margin="0,0,0,4" />
<TextBlock Text="To: me@example.com" Margin="0,0,0,4" />
<TextBlock Text="Subject: Meeting Tomorrow"
FontWeight="SemiBold"
Margin="0,0,0,12" />
<TextBlock Text="Hi there, Just a reminder about our meeting tomorrow at 10 AM. Please bring your project notes and status updates. Looking forward to discussing the progress."
TextWrapping="Wrap" />
</StackPanel>
</Border>

<!-- Message list (fills remaining space) -->
<ListBox>
<ListBoxItem Content="Meeting Tomorrow" />
<ListBoxItem Content="Project Update" />
<ListBoxItem Content="Weekly Report" />
<ListBoxItem Content="Team Lunch" />
<ListBoxItem Content="Vacation Request" />
</ListBox>
</DockPanel>

Behavior:

  • Elements are docked to the specified edge in the order they're declared
  • Each docked element takes up the full available space along its docked edge
  • Subsequent elements are arranged in the remaining space
  • The last child can optionally fill the remaining space

When to use:

  • For creating application layouts with headers, footers, and sidebars
  • For email clients, file explorers, and document editors
  • When you need a layout with multiple panels arranged around the edges
  • For creating master-detail views

Key properties:

  • DockPanel.Dock: Attached property that specifies which edge to dock to (Top, Bottom, Left, Right)
  • LastChildFill: If true (default), the last child fills the remaining space
  • Margin: Controls spacing between docked elements

Docking order considerations:

  • Elements are docked in the order they appear in XAML
  • The order can significantly affect the final layout
  • Consider docking Top/Bottom elements before Left/Right for more predictable layouts

UniformGrid

UniformGrid creates a grid where all cells have the same size, making it perfect for dashboards, calculators, game boards, and other grid-based UIs:

<!-- Calculator keypad -->
<Border Width="300"
Height="400"
Background="#f5f5f5"
Padding="10"
CornerRadius="8"
BoxShadow="0 4 8 0 #20000000">
<!-- Display area -->
<Grid RowDefinitions="Auto,*">
<Border Background="White"
Padding="16"
Margin="0,0,0,10"
CornerRadius="4">
<TextBlock Text="123.45"
FontSize="32"
FontWeight="SemiBold"
HorizontalAlignment="Right" />
</Border>

<!-- Keypad using UniformGrid -->
<UniformGrid Grid.Row="1"
Rows="5"
Columns="4"
RowSpacing="8"
ColumnSpacing="8">
<!-- Row 1 -->
<Button Content="C"
Background="#FFCDD2"
Foreground="#D32F2F"
FontWeight="SemiBold" />
<Button Content="±" />
<Button Content="%" />
<Button Content="÷"
Background="#E3F2FD"
Foreground="#1565C0"
FontWeight="SemiBold" />

<!-- Row 2 -->
<Button Content="7" />
<Button Content="8" />
<Button Content="9" />
<Button Content="×"
Background="#E3F2FD"
Foreground="#1565C0"
FontWeight="SemiBold" />

<!-- Row 3 -->
<Button Content="4" />
<Button Content="5" />
<Button Content="6" />
<Button Content="-"
Background="#E3F2FD"
Foreground="#1565C0"
FontWeight="SemiBold" />

<!-- Row 4 -->
<Button Content="1" />
<Button Content="2" />
<Button Content="3" />
<Button Content="+"
Background="#E3F2FD"
Foreground="#1565C0"
FontWeight="SemiBold" />

<!-- Row 5 -->
<Button Content="0" Grid.ColumnSpan="2" />
<Button Content="." />
<Button Content="="
Background="#E8F5E9"
Foreground="#2E7D32"
FontWeight="SemiBold" />
</UniformGrid>
</Grid>
</Border>
<!-- Color palette selector -->
<Border Background="White"
Padding="16"
CornerRadius="8"
Width="300"
BoxShadow="0 2 10 0 #20000000">
<StackPanel Spacing="16">
<TextBlock Text="Color Palette"
FontSize="18"
FontWeight="SemiBold" />

<!-- Primary colors -->
<StackPanel Spacing="8">
<TextBlock Text="Primary Colors"
FontSize="14"
Foreground="#757575" />

<UniformGrid Columns="5" RowSpacing="8" ColumnSpacing="8">
<Border Height="40" Background="#F44336" CornerRadius="4" />
<Border Height="40" Background="#E91E63" CornerRadius="4" />
<Border Height="40" Background="#9C27B0" CornerRadius="4" />
<Border Height="40" Background="#673AB7" CornerRadius="4" />
<Border Height="40" Background="#3F51B5" CornerRadius="4" />
<Border Height="40" Background="#2196F3" CornerRadius="4" />
<Border Height="40" Background="#03A9F4" CornerRadius="4" />
<Border Height="40" Background="#00BCD4" CornerRadius="4" />
<Border Height="40" Background="#009688" CornerRadius="4" />
<Border Height="40" Background="#4CAF50" CornerRadius="4" />
</UniformGrid>
</StackPanel>

<!-- Accent colors -->
<StackPanel Spacing="8">
<TextBlock Text="Accent Colors"
FontSize="14"
Foreground="#757575" />

<UniformGrid Columns="5" RowSpacing="8" ColumnSpacing="8">
<Border Height="40" Background="#8BC34A" CornerRadius="4" />
<Border Height="40" Background="#CDDC39" CornerRadius="4" />
<Border Height="40" Background="#FFEB3B" CornerRadius="4" />
<Border Height="40" Background="#FFC107" CornerRadius="4" />
<Border Height="40" Background="#FF9800" CornerRadius="4" />
</UniformGrid>
</StackPanel>

<!-- Grayscale -->
<StackPanel Spacing="8">
<TextBlock Text="Grayscale"
FontSize="14"
Foreground="#757575" />

<UniformGrid Columns="5" RowSpacing="8" ColumnSpacing="8">
<Border Height="40" Background="#212121" CornerRadius="4" />
<Border Height="40" Background="#616161" CornerRadius="4" />
<Border Height="40" Background="#9E9E9E" CornerRadius="4" />
<Border Height="40" Background="#E0E0E0" CornerRadius="4" />
<Border Height="40" Background="#F5F5F5" CornerRadius="4" />
</UniformGrid>
</StackPanel>
</StackPanel>
</Border>

Behavior:

  • Creates a grid with cells of equal size
  • Automatically distributes elements across rows and columns
  • Fills cells in row-major order (left to right, then top to bottom)
  • Adapts to available space while maintaining equal cell sizes

When to use:

  • For calculators, dialers, and keypads
  • For game boards (chess, tic-tac-toe, etc.)
  • For color palettes and swatch displays
  • For dashboard tiles of equal importance
  • When you need a simple grid without complex sizing rules

Key properties:

  • Rows, Columns: Number of rows and columns (at least one must be specified)
  • FirstColumn: Index of the first column to fill (useful for offsetting the first row)
  • RowSpacing, ColumnSpacing: Gap between rows and columns
  • HorizontalAlignment, VerticalAlignment: Control alignment within parent

Advantages over Grid:

  • Much simpler to use for grids with equal-sized cells
  • No need to define row and column definitions
  • Automatically adjusts when items are added or removed
  • Requires less XAML code

Grid System

The Grid panel is the most powerful and flexible layout container in Avalonia UI. It allows you to create complex, responsive layouts by dividing the available space into rows and columns with precise control over sizing and positioning.

Grid Layout Visualization

Basic Grid Layout

The Grid layout system enables sophisticated UI designs through a combination of row and column definitions, spanning, and alignment properties:

<!-- Dashboard layout with header, sidebar, and content areas -->
<Grid Margin="20"
RowDefinitions="Auto,*,Auto"
ColumnDefinitions="250,*"
RowSpacing="16"
ColumnSpacing="16">

<!-- Header area (spans all columns) -->
<Border Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Background="#1976D2"
Padding="16"
CornerRadius="4">
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Text="Dashboard"
Foreground="White"
FontSize="24"
FontWeight="SemiBold"
VerticalAlignment="Center" />

<StackPanel Grid.Column="2"
Orientation="Horizontal"
Spacing="12">
<Button Content="Settings"
Background="Transparent"
Foreground="White" />
<Button Content="Profile"
Background="White"
Foreground="#1976D2"
Padding="12,6" />
</StackPanel>
</Grid>
</Border>

<!-- Sidebar navigation -->
<Border Grid.Row="1"
Grid.Column="0"
Background="#f5f5f5"
Padding="12"
CornerRadius="4">
<StackPanel Spacing="8">
<TextBlock Text="NAVIGATION"
Foreground="#757575"
FontSize="12"
Margin="8,0,0,8" />

<Button Content="Dashboard"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12,8"
Background="#E3F2FD" />

<Button Content="Analytics"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12,8" />

<Button Content="Projects"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12,8" />

<Button Content="Team"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12,8" />

<TextBlock Text="SETTINGS"
Foreground="#757575"
FontSize="12"
Margin="8,16,0,8" />

<Button Content="Account"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12,8" />

<Button Content="Preferences"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12,8" />
</StackPanel>
</Border>

<!-- Main content area -->
<Border Grid.Row="1"
Grid.Column="1"
Background="White"
Padding="20"
CornerRadius="4">
<Grid RowDefinitions="Auto,*"
RowSpacing="16">
<TextBlock Text="Project Overview"
FontSize="20"
FontWeight="SemiBold" />

<!-- Content grid with cards -->
<Grid Grid.Row="1"
RowDefinitions="Auto,*"
ColumnDefinitions="*,*,*"
RowSpacing="16"
ColumnSpacing="16">

<!-- Statistics cards (first row) -->
<Border Grid.Row="0" Grid.Column="0"
Background="#E8F5E9"
Padding="16"
CornerRadius="4">
<StackPanel>
<TextBlock Text="Active Projects"
FontWeight="SemiBold" />
<TextBlock Text="24"
FontSize="32"
Foreground="#2E7D32" />
</StackPanel>
</Border>

<Border Grid.Row="0" Grid.Column="1"
Background="#E3F2FD"
Padding="16"
CornerRadius="4">
<StackPanel>
<TextBlock Text="Team Members"
FontWeight="SemiBold" />
<TextBlock Text="12"
FontSize="32"
Foreground="#1565C0" />
</StackPanel>
</Border>

<Border Grid.Row="0" Grid.Column="2"
Background="#FFF8E1"
Padding="16"
CornerRadius="4">
<StackPanel>
<TextBlock Text="Pending Tasks"
FontWeight="SemiBold" />
<TextBlock Text="18"
FontSize="32"
Foreground="#FF8F00" />
</StackPanel>
</Border>

<!-- Chart area (second row, spans all columns) -->
<Border Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Background="#FAFAFA"
Padding="16"
CornerRadius="4">
<TextBlock Text="Chart Area"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="18" />
</Border>
</Grid>
</Grid>
</Border>

<!-- Footer area (spans all columns) -->
<Border Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Background="#F5F5F5"
Padding="16"
CornerRadius="4">
<TextBlock Text="© 2023 Avalonia UI Dashboard"
HorizontalAlignment="Center"
Foreground="#757575" />
</Border>
</Grid>

Row and Column Definitions

Grid rows and columns can have different sizing behaviors to create flexible, responsive layouts:

<Grid>
<Grid.RowDefinitions>
<!-- Auto-sized row that fits its content -->
<RowDefinition Height="Auto" />

<!-- Takes all remaining space -->
<RowDefinition Height="*" />

<!-- Takes twice as much space as a single star -->
<RowDefinition Height="2*" />

<!-- Fixed height of 50 pixels -->
<RowDefinition Height="50" />

<!-- Combination of fixed and auto sizing -->
<RowDefinition Height="Auto" MinHeight="100" MaxHeight="200" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<!-- Fixed width of 200 pixels -->
<ColumnDefinition Width="200" />

<!-- Takes all remaining space -->
<ColumnDefinition Width="*" />

<!-- Takes 30% of the remaining space (3 parts out of 10) -->
<ColumnDefinition Width="3*" />

<!-- Takes 70% of the remaining space (7 parts out of 10) -->
<ColumnDefinition Width="7*" />
</Grid.ColumnDefinitions>
</Grid>

Sizing Options:

ValueDescriptionExample
AutoSizes to fit the content<RowDefinition Height="Auto" />
*Takes a proportional share of remaining space<ColumnDefinition Width="*" />
2*, 3*, etc.Takes a weighted proportional share<ColumnDefinition Width="2*" />
Fixed sizeUses a specific pixel size<RowDefinition Height="50" />
MixedCombines proportional with fixed<ColumnDefinition Width="100*" />

Constraints:

  • MinWidth/MinHeight: Minimum size regardless of content or available space
  • MaxWidth/MaxHeight: Maximum size regardless of content or available space

Spanning Multiple Rows or Columns

Elements can span multiple rows or columns to create more complex layouts:

<!-- Form layout with labels and inputs -->
<Grid Margin="20"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"
ColumnDefinitions="120,*"
RowSpacing="12"
ColumnSpacing="16">

<!-- Title spans both columns -->
<TextBlock Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Text="User Registration"
FontSize="24"
FontWeight="Bold"
Margin="0,0,0,20" />

<!-- Form fields with labels -->
<TextBlock Grid.Row="1" Grid.Column="0"
Text="First Name:"
VerticalAlignment="Center" />
<TextBox Grid.Row="1" Grid.Column="1" />

<TextBlock Grid.Row="2" Grid.Column="0"
Text="Last Name:"
VerticalAlignment="Center" />
<TextBox Grid.Row="2" Grid.Column="1" />

<TextBlock Grid.Row="3" Grid.Column="0"
Text="Email:"
VerticalAlignment="Center" />
<TextBox Grid.Row="3" Grid.Column="1" />

<!-- Address spans the full width with indentation -->
<TextBlock Grid.Row="4" Grid.Column="0"
Text="Address:"
VerticalAlignment="Top" />
<TextBox Grid.Row="4" Grid.Column="1"
Height="80"
AcceptsReturn="True"
TextWrapping="Wrap" />

<!-- Buttons in the last row, right-aligned -->
<StackPanel Grid.Row="5"
Grid.Column="0"
Grid.ColumnSpan="2"
Orientation="Horizontal"
HorizontalAlignment="Right"
Spacing="12"
Margin="0,20,0,0">
<Button Content="Cancel"
Width="100"
Background="Transparent"
BorderBrush="#BDBDBD"
BorderThickness="1" />
<Button Content="Submit"
Width="100"
Background="#1976D2"
Foreground="White" />
</StackPanel>
</Grid>

Spanning Properties:

  • Grid.RowSpan: Number of rows the element should span
  • Grid.ColumnSpan: Number of columns the element should span

Positioning Properties:

  • Grid.Row: Zero-based row index where the element should be placed
  • Grid.Column: Zero-based column index where the element should be placed

Grid Splitters

GridSplitter allows users to resize rows or columns at runtime, creating adjustable layouts:

<!-- Code editor layout with resizable panels -->
<Grid Background="#252526">
<!-- Define columns for file explorer, editor, and properties panel -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" MinWidth="150" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" MinWidth="300" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="300" MinWidth="200" />
</Grid.ColumnDefinitions>

<!-- Define rows for toolbar, content area, and status bar -->
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="200" MinHeight="100" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>

<!-- Toolbar (spans all columns) -->
<Border Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="5"
Background="#333333">
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
Margin="10,0"
Spacing="8">
<Button Content="File"
Background="Transparent"
Foreground="White" />
<Button Content="Edit"
Background="Transparent"
Foreground="White" />
<Button Content="View"
Background="Transparent"
Foreground="White" />
<Button Content="Help"
Background="Transparent"
Foreground="White" />
</StackPanel>
</Border>

<!-- File explorer panel -->
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="0"
Background="#252526">
<TextBlock Text="File Explorer"
Foreground="White"
Margin="10" />
</Border>

<!-- Vertical splitter between file explorer and editor -->
<GridSplitter Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="1"
Width="5"
Background="#505050"
HorizontalAlignment="Center"
VerticalAlignment="Stretch" />

<!-- Code editor area -->
<Border Grid.Row="1"
Grid.Column="2"
Background="#1E1E1E">
<TextBlock Text="Code Editor"
Foreground="White"
Margin="10" />
</Border>

<!-- Vertical splitter between editor and properties -->
<GridSplitter Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="3"
Width="5"
Background="#505050"
HorizontalAlignment="Center"
VerticalAlignment="Stretch" />

<!-- Properties panel -->
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="4"
Background="#252526">
<TextBlock Text="Properties"
Foreground="White"
Margin="10" />
</Border>

<!-- Horizontal splitter between editor and console -->
<GridSplitter Grid.Row="2"
Grid.Column="2"
Height="5"
Background="#505050"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />

<!-- Console/output panel -->
<Border Grid.Row="3"
Grid.Column="2"
Background="#1E1E1E">
<TextBlock Text="Console Output"
Foreground="White"
Margin="10" />
</Border>

<!-- Status bar (spans all columns) -->
<Border Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="5"
Background="#007ACC">
<TextBlock Text="Ready"
Foreground="White"
Margin="10,0"
VerticalAlignment="Center" />
</Border>
</Grid>

Vertical GridSplitter:

  • Place in its own column with Width="Auto"
  • Set HorizontalAlignment="Center" and VerticalAlignment="Stretch"
  • Typical width is 5-10 pixels

Horizontal GridSplitter:

  • Place in its own row with Height="Auto"
  • Set HorizontalAlignment="Stretch" and VerticalAlignment="Center"
  • Typical height is 5-10 pixels

Styling GridSplitters:

  • Use Background to change the color
  • Use BorderBrush and BorderThickness to add borders
  • Consider adding a visual indicator (like a grip pattern) for better usability

Best Practices:

  • Set MinWidth/MinHeight on adjacent rows/columns to prevent collapsing
  • Consider using MaxWidth/MaxHeight to prevent excessive resizing
  • Provide visual feedback when the splitter is being dragged

Responsive Design

Creating responsive layouts in Avalonia UI involves designing interfaces that adapt to different screen sizes, orientations, and device capabilities. Unlike web development, Avalonia doesn't have built-in media queries, but it offers powerful tools to create adaptive UIs.

Responsive Design

Adaptive Layout Principles

When designing responsive Avalonia applications, follow these key principles:

  1. Fluid over Fixed: Use proportional sizing instead of fixed pixels
  2. Constrain when Necessary: Set minimum and maximum sizes to maintain usability
  3. Progressive Disclosure: Show more information as screen space increases
  4. Adapt Layout Structure: Change the arrangement of elements based on available space
  5. Maintain Usability: Ensure touch targets remain large enough on small screens

Adaptive Layout Techniques

1. Proportional Sizing with Star Notation

Use star sizing (*) to create fluid layouts that adapt to available space:

<!-- Responsive dashboard layout -->
<Grid Margin="16">
<!-- Header row takes what it needs, content row takes the rest -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- Left sidebar takes 1/4, content takes 3/4 of available width -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="200" />
<ColumnDefinition Width="3*" MinWidth="400" />
</Grid.ColumnDefinitions>

<!-- Header spans both columns -->
<Border Grid.Row="0"
Grid.ColumnSpan="2"
Background="#1976D2"
Padding="16"
Margin="0,0,0,16"
CornerRadius="4">
<TextBlock Text="Dashboard"
Foreground="White"
FontSize="24" />
</Border>

<!-- Navigation panel -->
<Border Grid.Row="1"
Grid.Column="0"
Background="#f5f5f5"
Padding="16"
Margin="0,0,16,0"
CornerRadius="4">
<TextBlock Text="Navigation" />
</Border>

<!-- Content panel -->
<Border Grid.Row="1"
Grid.Column="1"
Background="White"
Padding="16"
CornerRadius="4">
<TextBlock Text="Main Content" />
</Border>
</Grid>

2. Min/Max Constraints

Set minimum and maximum sizes to ensure elements remain usable regardless of screen size:

<!-- Responsive card that won't get too wide or too narrow -->
<Border Background="White"
CornerRadius="8"
BoxShadow="0 2 10 0 #20000000"
Padding="20"
Margin="10"
MinWidth="250"
MaxWidth="500"
HorizontalAlignment="Stretch">
<StackPanel Spacing="12">
<TextBlock Text="Product Card"
FontSize="18"
FontWeight="SemiBold" />
<TextBlock Text="This card maintains a reasonable width range to ensure optimal readability on all screen sizes."
TextWrapping="Wrap" />
<Button Content="Learn More"
HorizontalAlignment="Right" />
</StackPanel>
</Border>

3. Alignment Properties

Use alignment properties to position elements appropriately within their containers:

<!-- Responsive button bar that adapts to container width -->
<Border Background="#f5f5f5"
Padding="16"
CornerRadius="4">
<StackPanel Orientation="Horizontal"
Spacing="8"
HorizontalAlignment="Right">
<Button Content="Cancel"
Padding="16,8"
Background="Transparent"
BorderBrush="#BDBDBD"
BorderThickness="1" />
<Button Content="Save"
Padding="16,8"
Background="#1976D2"
Foreground="White" />
</StackPanel>
</Border>

4. Nested Layout Containers

Combine different layout panels to create complex responsive layouts:

<!-- Responsive application layout with header, sidebar, and content -->
<DockPanel>
<!-- Fixed-height header -->
<Border DockPanel.Dock="Top"
Height="60"
Background="#1976D2"
Padding="16,0">
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Text="Application"
Foreground="White"
FontSize="20"
VerticalAlignment="Center" />

<StackPanel Grid.Column="2"
Orientation="Horizontal"
Spacing="16"
VerticalAlignment="Center">
<Button Content="Settings"
Background="Transparent"
Foreground="White" />
<Button Content="Profile"
Background="Transparent"
Foreground="White" />
</StackPanel>
</Grid>
</Border>

<!-- Flexible content area with sidebar -->
<Grid>
<Grid.ColumnDefinitions>
<!-- Sidebar with minimum width -->
<ColumnDefinition Width="250" MinWidth="200" />
<!-- Splitter -->
<ColumnDefinition Width="Auto" />
<!-- Content area takes remaining space -->
<ColumnDefinition Width="*" MinWidth="400" />
</Grid.ColumnDefinitions>

<!-- Sidebar with scrolling -->
<ScrollViewer Grid.Column="0"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<StackPanel Margin="16">
<TextBlock Text="Navigation"
FontWeight="SemiBold"
Margin="0,0,0,16" />

<!-- Navigation items -->
<ListBox>
<ListBoxItem Content="Dashboard" />
<ListBoxItem Content="Projects" />
<ListBoxItem Content="Tasks" />
<ListBoxItem Content="Calendar" />
<ListBoxItem Content="Reports" />
</ListBox>
</StackPanel>
</ScrollViewer>

<!-- Resizable splitter -->
<GridSplitter Grid.Column="1"
Width="5"
Background="#e0e0e0"
HorizontalAlignment="Center" />

<!-- Main content with scrolling -->
<ScrollViewer Grid.Column="2"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<StackPanel Margin="16">
<TextBlock Text="Main Content"
FontWeight="SemiBold"
FontSize="20"
Margin="0,0,0,16" />

<!-- Content goes here -->
<TextBlock Text="This area contains the main content and scrolls independently from the sidebar."
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
</Grid>
</DockPanel>

Programmatic Responsive Adaptation

For more complex responsive behavior, you can adapt layouts programmatically based on window size:

using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;

public partial class MainWindow : Window
{
// Breakpoints for different screen sizes
private const double NARROW_WIDTH_THRESHOLD = 600;
private const double MEDIUM_WIDTH_THRESHOLD = 1024;

public MainWindow()
{
InitializeComponent();

// Subscribe to window size changed event
this.GetObservable(Window.ClientSizeProperty).Subscribe(OnWindowSizeChanged);
}

/// <summary>
/// Handles window size changes and adapts the layout accordingly
/// </summary>
/// <param name="newSize">The new client size of the window</param>
private void OnWindowSizeChanged(Size newSize)
{
double width = newSize.Width;

if (width < NARROW_WIDTH_THRESHOLD)
{
// Narrow layout (mobile-like)
ApplyNarrowLayout();
}
else if (width < MEDIUM_WIDTH_THRESHOLD)
{
// Medium layout (tablet-like)
ApplyMediumLayout();
}
else
{
// Wide layout (desktop)
ApplyWideLayout();
}
}

/// <summary>
/// Applies a layout optimized for narrow screens (e.g., mobile devices)
/// </summary>
private void ApplyNarrowLayout()
{
// Hide sidebar
if (MainGrid.ColumnDefinitions.Count > 0)
{
MainGrid.ColumnDefinitions[0].Width = new GridLength(0);
}

// Stack navigation horizontally in header
if (NavigationPanel != null)
{
NavigationPanel.Orientation = Orientation.Horizontal;
DockPanel.SetDock(NavigationPanel, Dock.Top);
}

// Adjust content margins for better use of space
if (ContentPanel != null)
{
ContentPanel.Margin = new Thickness(10);
}

// Adjust font sizes for readability on small screens
Resources["HeaderFontSize"] = 18.0;
Resources["BodyFontSize"] = 14.0;
}

/// <summary>
/// Applies a layout optimized for medium-sized screens (e.g., tablets)
/// </summary>
private void ApplyMediumLayout()
{
// Compact sidebar
if (MainGrid.ColumnDefinitions.Count > 0)
{
MainGrid.ColumnDefinitions[0].Width = new GridLength(200);
}

// Vertical navigation in sidebar
if (NavigationPanel != null)
{
NavigationPanel.Orientation = Orientation.Vertical;
DockPanel.SetDock(NavigationPanel, Dock.Left);
}

// Adjust content margins
if (ContentPanel != null)
{
ContentPanel.Margin = new Thickness(15);
}

// Adjust font sizes
Resources["HeaderFontSize"] = 20.0;
Resources["BodyFontSize"] = 14.0;
}

/// <summary>
/// Applies a layout optimized for wide screens (e.g., desktops)
/// </summary>
private void ApplyWideLayout()
{
// Full sidebar
if (MainGrid.ColumnDefinitions.Count > 0)
{
MainGrid.ColumnDefinitions[0].Width = new GridLength(250);
}

// Vertical navigation with more details
if (NavigationPanel != null)
{
NavigationPanel.Orientation = Orientation.Vertical;
DockPanel.SetDock(NavigationPanel, Dock.Left);
}

// Comfortable content margins
if (ContentPanel != null)
{
ContentPanel.Margin = new Thickness(20);
}

// Larger font sizes for comfortable reading
Resources["HeaderFontSize"] = 24.0;
Resources["BodyFontSize"] = 16.0;
}
}

Responsive Design with Styles and Visual States

You can use styles and visual states to create responsive layouts without code-behind:

<UserControl.Styles>
<!-- Base styles for all screen sizes -->
<Style Selector="Grid#MainLayout">
<Setter Property="RowDefinitions" Value="Auto,*" />
</Style>

<!-- Styles for wide screens (default) -->
<Style Selector="Grid#MainLayout">
<Setter Property="ColumnDefinitions" Value="250,*" />
</Style>

<Style Selector="StackPanel#NavigationPanel">
<Setter Property="Grid.Row" Value="1" />
<Setter Property="Grid.Column" Value="0" />
<Setter Property="Orientation" Value="Vertical" />
<Setter Property="Spacing" Value="8" />
</Style>

<Style Selector="Border#ContentPanel">
<Setter Property="Grid.Row" Value="1" />
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Margin" Value="16,0,0,0" />
</Style>

<!-- Styles for narrow screens (applied via code) -->
<Style Selector="Grid#MainLayout.narrow">
<Setter Property="ColumnDefinitions" Value="*" />
</Style>

<Style Selector="StackPanel#NavigationPanel.narrow">
<Setter Property="Grid.Row" Value="0" />
<Setter Property="Grid.Column" Value="0" />
<Setter Property="Orientation" Value="Horizontal" />
<Setter Property="Spacing" Value="4" />
<Setter Property="Margin" Value="0,0,0,16" />
</Style>

<Style Selector="Border#ContentPanel.narrow">
<Setter Property="Grid.Row" Value="1" />
<Setter Property="Grid.Column" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>

<!-- Styles for buttons in different layouts -->
<Style Selector="Button.navButton">
<Setter Property="Padding" Value="16,8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>

<Style Selector="Button.navButton.narrow">
<Setter Property="Padding" Value="8,8" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</UserControl.Styles>

<Grid x:Name="MainLayout">
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
Text="Application Header"
FontSize="24"
Margin="0,0,0,16" />

<StackPanel x:Name="NavigationPanel">
<Button Content="Dashboard" Classes="navButton" />
<Button Content="Projects" Classes="navButton" />
<Button Content="Reports" Classes="navButton" />
<Button Content="Settings" Classes="navButton" />
</StackPanel>

<Border x:Name="ContentPanel"
Background="White"
Padding="16"
CornerRadius="4">
<TextBlock Text="Main Content Area" />
</Border>
</Grid>
// In code-behind, apply the appropriate classes based on window size
private void OnWindowSizeChanged(Size newSize)
{
bool isNarrow = newSize.Width < 800;

// Apply or remove the "narrow" class based on window width
if (isNarrow)
{
MainLayout.Classes.Add("narrow");
NavigationPanel.Classes.Add("narrow");
ContentPanel.Classes.Add("narrow");

foreach (var button in NavigationPanel.Children.OfType<Button>())
{
button.Classes.Add("narrow");
}
}
else
{
MainLayout.Classes.Remove("narrow");
NavigationPanel.Classes.Remove("narrow");
ContentPanel.Classes.Remove("narrow");

foreach (var button in NavigationPanel.Children.OfType<Button>())
{
button.Classes.Remove("narrow");
}
}
}

Best Practices for Responsive Design

  1. Design for Mobile First: Start with a layout that works on small screens, then enhance for larger screens
  2. Use Relative Units: Prefer star sizing (*) over fixed pixel sizes
  3. Set Appropriate Constraints: Use MinWidth/MinHeight and MaxWidth/MaxHeight
  4. Test on Multiple Screen Sizes: Regularly test your UI at different resolutions
  5. Consider Touch Targets: Ensure interactive elements are large enough for touch input (at least 40×40 pixels)
  6. Use ScrollViewers Wisely: Allow content to scroll when it doesn't fit
  7. Simplify for Small Screens: Hide or collapse less important elements on smaller screens
  8. Maintain Visual Hierarchy: Ensure important elements remain prominent at all screen sizes

Example of Responsive Navigation

<StackPanel x:Name="NavigationPanel" Spacing="5" Margin="10">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Height" Value="40" />
<Setter Property="Margin" Value="0,5" />
</Style>

<Style Selector="StackPanel.compact > TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</StackPanel.Styles>

<!-- Panel content -->
<Button Content="Home" />
<Button Content="Products" />
<Button Content="Services" />
<Button Content="About" />
<Button Content="Contact" />
</StackPanel>
using Avalonia;
using Avalonia.Controls;
using System;
using System.Diagnostics;

namespace MyAvaloniaApp.Views;

/// <summary>
/// Handles the window size changed event to apply visual states based on window width.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">Event data containing property information.</param>
/// <remarks>
/// This method demonstrates how to use Avalonia's styling system to create responsive layouts
/// by applying CSS-like classes to elements based on the window size.
/// </remarks>
private void OnWindowPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
// Check if the Bounds property has changed (window resized)
if (e.Property == Window.BoundsProperty)
{
try
{
// Get the new window bounds
var bounds = (Rect)e.NewValue;
double width = bounds.Width;

// Find the responsive panel
var responsivePanel = this.FindControl<StackPanel>("ResponsivePanel");
if (responsivePanel == null)
{
Debug.WriteLine("Warning: ResponsivePanel not found");
return;
}

// Define breakpoint for compact layout
const double compactThreshold = 600;

// Apply appropriate class based on window width
if (width < compactThreshold)
{
// Switch to compact layout for narrow windows
if (!responsivePanel.Classes.Contains("compact"))
{
responsivePanel.Classes.Add("compact");
Debug.WriteLine("Applied compact layout");
}
}
else
{
// Switch to normal layout for wider windows
if (responsivePanel.Classes.Contains("compact"))
{
responsivePanel.Classes.Remove("compact");
Debug.WriteLine("Applied normal layout");
}
}

// You can also apply different classes for different breakpoints
// For example:
// if (width < 400) responsivePanel.Classes.Add("very-compact");
// else responsivePanel.Classes.Remove("very-compact");
}
catch (Exception ex)
{
// Log any errors that occur during layout adjustment
Debug.WriteLine($"Error adjusting layout: {ex.Message}");
}
}
}

Layout Best Practices

Follow these best practices for effective layout management in Avalonia UI:

1. Choose the Right Layout Container

  • Use StackPanel for simple linear arrangements
  • Use WrapPanel for collections that need to wrap
  • Use DockPanel for layouts with headers, footers, and sidebars
  • Use Grid for complex layouts with precise control
  • Use Canvas only when absolute positioning is necessary

2. Avoid Fixed Sizes When Possible

Prefer relative sizing (*) over fixed pixel sizes to create layouts that adapt to different screen sizes:

<!-- Good -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
</Grid>

<!-- Avoid -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="600" />
</Grid.ColumnDefinitions>
</Grid>

3. Use Margin and Padding Appropriately

  • Margin: Space outside the control (affects layout)
  • Padding: Space inside the control (affects content)
<Button Content="Spaced Button" Margin="10" Padding="20,10" />

4. Consider Layout Performance

  • Deeply nested layouts can impact performance
  • Grid is more efficient than multiple nested panels
  • Use Virtualization for large collections

5. Design for Different Screen Orientations

Create layouts that work well in both portrait and landscape orientations:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<TextBlock Grid.Row="0" Text="Header" />
<ScrollViewer Grid.Row="1">
<WrapPanel>
<!-- Content that wraps naturally -->
</WrapPanel>
</ScrollViewer>
</Grid>

6. Use ScrollViewer for Content That Might Overflow

Wrap content that might be larger than the available space in a ScrollViewer:

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel>
<!-- Potentially large content -->
</StackPanel>
</ScrollViewer>

7. Test on Different Screen Sizes

Regularly test your layouts on different screen sizes and resolutions to ensure they adapt correctly.

Example: Responsive Dashboard Layout

Here's an example of a responsive dashboard layout that adapts to different screen sizes:

<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.DashboardWindow"
Title="Responsive Dashboard"
Width="1000" Height="600">

<DockPanel>
<!-- Header -->
<Border DockPanel.Dock="Top" Background="#333" Height="60">
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Grid.Column="0" Text="Dashboard" Foreground="White"
VerticalAlignment="Center" Margin="20,0" FontSize="20" />
<StackPanel Grid.Column="2" Orientation="Horizontal" Margin="20,0">
<Button Content="Settings" Margin="5,0" />
<Button Content="Profile" Margin="5,0" />
</StackPanel>
</Grid>
</Border>

<!-- Sidebar and Main Content -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220" MinWidth="150" x:Name="SidebarColumn" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<!-- Sidebar -->
<Border Grid.Column="0" Background="#f0f0f0">
<StackPanel Margin="10">
<TextBlock Text="Navigation" FontWeight="Bold" Margin="0,0,0,10" />
<Button Content="Dashboard" HorizontalAlignment="Stretch" Margin="0,5" />
<Button Content="Reports" HorizontalAlignment="Stretch" Margin="0,5" />
<Button Content="Analytics" HorizontalAlignment="Stretch" Margin="0,5" />
<Button Content="Users" HorizontalAlignment="Stretch" Margin="0,5" />
<Button Content="Settings" HorizontalAlignment="Stretch" Margin="0,5" />
</StackPanel>
</Border>

<!-- Splitter -->
<GridSplitter Grid.Column="1" Width="5" Background="#ccc"
HorizontalAlignment="Center" VerticalAlignment="Stretch" />

<!-- Main Content -->
<Grid Grid.Column="2" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- Dashboard Header -->
<TextBlock Grid.Row="0" Text="Dashboard Overview" FontSize="24" Margin="0,0,0,10" />

<!-- Dashboard Content -->
<ScrollViewer Grid.Row="1">
<WrapPanel>
<!-- Dashboard Cards -->
<Border Width="300" Height="150" Margin="5" Background="#e3f2fd" CornerRadius="5">
<StackPanel Margin="15">
<TextBlock Text="Users" FontWeight="Bold" />
<TextBlock Text="1,254" FontSize="32" Margin="0,10,0,0" />
<TextBlock Text="↑ 12% from last month" Foreground="Green" />
</StackPanel>
</Border>

<Border Width="300" Height="150" Margin="5" Background="#e8f5e9" CornerRadius="5">
<StackPanel Margin="15">
<TextBlock Text="Revenue" FontWeight="Bold" />
<TextBlock Text="$45,678" FontSize="32" Margin="0,10,0,0" />
<TextBlock Text="↑ 8% from last month" Foreground="Green" />
</StackPanel>
</Border>

<Border Width="300" Height="150" Margin="5" Background="#fff3e0" CornerRadius="5">
<StackPanel Margin="15">
<TextBlock Text="Orders" FontWeight="Bold" />
<TextBlock Text="867" FontSize="32" Margin="0,10,0,0" />
<TextBlock Text="↓ 3% from last month" Foreground="Red" />
</StackPanel>
</Border>

<Border Width="300" Height="150" Margin="5" Background="#f3e5f5" CornerRadius="5">
<StackPanel Margin="15">
<TextBlock Text="Conversion Rate" FontWeight="Bold" />
<TextBlock Text="24.8%" FontSize="32" Margin="0,10,0,0" />
<TextBlock Text="↑ 2% from last month" Foreground="Green" />
</StackPanel>
</Border>

<!-- More cards can be added here -->
</WrapPanel>
</ScrollViewer>
</Grid>
</Grid>
</DockPanel>
</Window>

In the code-behind, you can add logic to adapt the layout for smaller screens:

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
using System.Diagnostics;

namespace MyAvaloniaApp.Views;

/// <summary>
/// A responsive dashboard window that adapts its layout based on window size.
/// </summary>
/// <remarks>
/// This class demonstrates how to create a responsive layout in Avalonia UI
/// by adjusting UI elements when the window size changes.
/// </remarks>
public partial class DashboardWindow : Window
{
// Constants for layout breakpoints
private const double SMALL_SCREEN_WIDTH = 800;
private const double MEDIUM_SCREEN_WIDTH = 1200;

// Constants for sidebar widths
private const double SIDEBAR_NORMAL_WIDTH = 220;
private const double SIDEBAR_COMPACT_WIDTH = 150;
private const double SIDEBAR_HIDDEN_WIDTH = 0;

// Track the current layout state
private enum LayoutState { Small, Medium, Large }
private LayoutState _currentLayout = LayoutState.Large;

/// <summary>
/// Initializes a new instance of the <see cref="DashboardWindow"/> class.
/// </summary>
public DashboardWindow()
{
InitializeComponent();

// Subscribe to property changes to detect window size changes
this.PropertyChanged += OnWindowPropertyChanged;

// Initial layout setup based on starting window size
ApplyLayoutForCurrentSize();

// Optional: Log window creation for debugging
Debug.WriteLine($"Dashboard window created with size: {this.ClientSize}");
}

/// <summary>
/// Handles property changes on the window, particularly focusing on size changes.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">Event data containing property information.</param>
private void OnWindowPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
// Check if the Bounds property has changed (window resized)
if (e.Property == Window.BoundsProperty)
{
try
{
// Get the new window bounds
var bounds = (Rect)e.NewValue;

// Log the size change
Debug.WriteLine($"Window size changed: Width={bounds.Width}, Height={bounds.Height}");

// Apply appropriate layout based on the new size
ApplyLayoutForSize(bounds.Width);
}
catch (Exception ex)
{
// Log any errors that occur during layout adjustment
Debug.WriteLine($"Error adjusting layout: {ex.Message}");
}
}
}

/// <summary>
/// Applies the appropriate layout based on the current window size.
/// </summary>
private void ApplyLayoutForCurrentSize()
{
if (this.ClientSize.Width > 0)
{
ApplyLayoutForSize(this.ClientSize.Width);
}
}

/// <summary>
/// Applies the appropriate layout based on the specified width.
/// </summary>
/// <param name="width">The width to base the layout decisions on.</param>
private void ApplyLayoutForSize(double width)
{
// Determine which layout to apply based on width
LayoutState newLayout;

if (width < SMALL_SCREEN_WIDTH)
{
newLayout = LayoutState.Small;
}
else if (width < MEDIUM_SCREEN_WIDTH)
{
newLayout = LayoutState.Medium;
}
else
{
newLayout = LayoutState.Large;
}

// Only apply changes if the layout state has changed
if (newLayout != _currentLayout)
{
_currentLayout = newLayout;
ApplyLayout(newLayout);
}
}

/// <summary>
/// Applies the specified layout state to the UI elements.
/// </summary>
/// <param name="state">The layout state to apply.</param>
private void ApplyLayout(LayoutState state)
{
// Find the sidebar column definition
var sidebarColumn = this.FindControl<ColumnDefinition>("SidebarColumn");
if (sidebarColumn == null)
{
Debug.WriteLine("Warning: SidebarColumn not found");
return;
}

// Apply layout changes based on the state
switch (state)
{
case LayoutState.Small:
// For small screens, hide the sidebar completely
sidebarColumn.Width = new GridLength(SIDEBAR_HIDDEN_WIDTH);

// You could also adjust other elements for small screens
// For example, change font sizes, button sizes, etc.
AdjustCardSizes(250, 130); // Smaller cards
ShowCompactHeader(true);
break;

case LayoutState.Medium:
// For medium screens, show a compact sidebar
sidebarColumn.Width = new GridLength(SIDEBAR_COMPACT_WIDTH);
AdjustCardSizes(280, 140); // Medium cards
ShowCompactHeader(false);
break;

case LayoutState.Large:
default:
// For large screens, show the full sidebar
sidebarColumn.Width = new GridLength(SIDEBAR_NORMAL_WIDTH);
AdjustCardSizes(300, 150); // Full-size cards
ShowCompactHeader(false);
break;
}

Debug.WriteLine($"Applied layout: {state}");
}

/// <summary>
/// Adjusts the size of dashboard cards.
/// </summary>
/// <param name="width">The width to set for the cards.</param>
/// <param name="height">The height to set for the cards.</param>
private void AdjustCardSizes(double width, double height)
{
// Find all dashboard card elements and adjust their sizes
// This is just an example - you would need to implement this based on your actual UI structure
// For example:
// var cards = this.FindChildren<Border>("DashboardCard");
// foreach (var card in cards)
// {
// card.Width = width;
// card.Height = height;
// }
}

/// <summary>
/// Shows or hides the compact version of the header.
/// </summary>
/// <param name="showCompact">Whether to show the compact header.</param>
private void ShowCompactHeader(bool showCompact)
{
// Find header elements and adjust their visibility
// For example:
// var fullHeader = this.FindControl<Panel>("FullHeader");
// var compactHeader = this.FindControl<Panel>("CompactHeader");
//
// if (fullHeader != null && compactHeader != null)
// {
// fullHeader.IsVisible = !showCompact;
// compactHeader.IsVisible = showCompact;
// }
}
}

This dashboard layout demonstrates several responsive design techniques:

  • Using DockPanel for the overall layout with header and main content
  • Using Grid with flexible column definitions for the sidebar and content
  • Using GridSplitter to allow users to resize the sidebar
  • Using WrapPanel for dashboard cards that automatically wrap to new lines
  • Adapting the layout for smaller screens by hiding the sidebar

In the next section, we'll explore data binding and the MVVM pattern in Avalonia UI.