Skip to main content

Layoutable

The Layoutable class implements layout-related functionality for visual controls in Avalonia. It’s the base class for all controls that participate in the layout system.

Namespace

Avalonia.Layout

Inheritance

Object → AvaloniaObject → Visual → Layoutable

Enumerations

HorizontalAlignment

Stretch
enum
The control stretches to fill the width of the parent.
Left
enum
The control aligns to the left of the parent.
Center
enum
The control centers itself horizontally in the parent.
Right
enum
The control aligns to the right of the parent.

VerticalAlignment

Stretch
enum
The control stretches to fill the height of the parent.
Top
enum
The control aligns to the top of the parent.
Center
enum
The control centers itself vertically in the parent.
Bottom
enum
The control aligns to the bottom of the parent.

Properties

Width
double
Gets or sets the width of the control. Default is double.NaN (auto-size).
Height
double
Gets or sets the height of the control. Default is double.NaN (auto-size).
MinWidth
double
Gets or sets the minimum width of the control. Default is 0.
MinHeight
double
Gets or sets the minimum height of the control. Default is 0.
MaxWidth
double
Gets or sets the maximum width of the control. Default is double.PositiveInfinity.
MaxHeight
double
Gets or sets the maximum height of the control. Default is double.PositiveInfinity.
Margin
Thickness
Gets or sets the margin around the control.Margin is space outside the control that separates it from adjacent controls.
HorizontalAlignment
HorizontalAlignment
Gets or sets how the control aligns itself horizontally in its parent. Default is Stretch.
VerticalAlignment
VerticalAlignment
Gets or sets how the control aligns itself vertically in its parent. Default is Stretch.
DesiredSize
Size
Gets the size that this control computed during the measure pass.This is a read-only property set by the Measure method.
UseLayoutRounding
bool
Gets or sets whether layout rounding is enabled. Default is true.Layout rounding snaps layout to pixel boundaries for crisp rendering.

Methods

Measure
void
Measures the control and its children.Parameters:
  • availableSize (Size): The available size for the control
This is the first pass of the layout process.
Arrange
void
Arranges the control and its children.Parameters:
  • rect (Rect): The final size and position for the control
This is the second pass of the layout process.
InvalidateMeasure
void
Invalidates the measurement state of the control.Call this when a property changes that affects the control’s desired size.
InvalidateArrange
void
Invalidates the arrangement state of the control.Call this when a property changes that affects how children are arranged.

Events

EffectiveViewportChanged
EventHandler<EffectiveViewportChangedEventArgs>
Occurs when the control’s effective viewport changes.Useful for implementing virtualization.
LayoutUpdated
EventHandler
Occurs when a layout pass completes for the control.

Usage Examples

Setting Size Properties

<!-- Fixed size -->
<Button Width="100" Height="40" Content="Click Me" />

<!-- Auto size (default) -->
<Button Content="Auto-sized" />

<!-- Min/Max constraints -->
<TextBox MinWidth="200" MaxWidth="400" />

<!-- Size constraints -->
<Border MinWidth="100" MinHeight="100" 
        MaxWidth="500" MaxHeight="300"
        Background="LightBlue" />

Alignment

<Grid>
    <!-- Left-aligned -->
    <Button HorizontalAlignment="Left" 
            VerticalAlignment="Top"
            Content="Top Left" />
    
    <!-- Centered -->
    <Button HorizontalAlignment="Center" 
            VerticalAlignment="Center"
            Content="Centered" />
    
    <!-- Stretch (fill available space) -->
    <Button HorizontalAlignment="Stretch" 
            VerticalAlignment="Stretch"
            Content="Fill" />
</Grid>

Margin

<!-- Uniform margin -->
<Button Margin="10" Content="10px margin all sides" />

<!-- Horizontal and vertical -->
<Button Margin="10,5" Content="10px horizontal, 5px vertical" />

<!-- Individual sides -->
<Button Margin="10,5,15,20" Content="Left, Top, Right, Bottom" />

<!-- Using StackPanel spacing instead -->
<StackPanel Spacing="8">
    <Button Content="Button 1" />
    <Button Content="Button 2" />
    <Button Content="Button 3" />
</StackPanel>

Code-Behind

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

// Set size
button.Width = 150;
button.Height = 40;

// Set constraints
button.MinWidth = 100;
button.MaxWidth = 300;

// Set alignment
button.HorizontalAlignment = HorizontalAlignment.Center;
button.VerticalAlignment = VerticalAlignment.Top;

// Set margin
button.Margin = new Thickness(10); // All sides
button.Margin = new Thickness(10, 5); // Horizontal, Vertical
button.Margin = new Thickness(10, 5, 15, 20); // Left, Top, Right, Bottom

// Get desired size after measure
button.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var desiredSize = button.DesiredSize;

Custom Layout

protected override Size MeasureOverride(Size availableSize)
{
    var desiredSize = new Size();
    
    foreach (var child in Children)
    {
        // Measure each child
        child.Measure(availableSize);
        
        // Accumulate desired size
        desiredSize = desiredSize.WithWidth(
            Math.Max(desiredSize.Width, child.DesiredSize.Width)
        );
        desiredSize = desiredSize.WithHeight(
            desiredSize.Height + child.DesiredSize.Height
        );
    }
    
    return desiredSize;
}

protected override Size ArrangeOverride(Size finalSize)
{
    double y = 0;
    
    foreach (var child in Children)
    {
        // Arrange each child
        var childBounds = new Rect(0, y, finalSize.Width, child.DesiredSize.Height);
        child.Arrange(childBounds);
        
        y += child.DesiredSize.Height;
    }
    
    return finalSize;
}

Layout Process

The layout process consists of two passes:
  1. Measure Pass: Controls calculate their desired size based on available space
  2. Arrange Pass: Controls are positioned and given their final size
┌─────────────────────────────────────┐
│         Measure Pass                │
│  (Bottom-up: children → parent)     │
│                                     │
│  1. Parent calls Child.Measure()    │
│  2. Child measures itself           │
│  3. Child returns DesiredSize       │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│         Arrange Pass                │
│   (Top-down: parent → children)     │
│                                     │
│  1. Parent calls Child.Arrange()    │
│  2. Child positions itself          │
│  3. Child arranges its children     │
└─────────────────────────────────────┘

Best Practices

Use Width and Height only when necessary. Let controls size themselves based on content.
<!-- Good: Auto-sized based on content -->
<Button Content="Click Me" />

<!-- Use only when needed -->
<Button Width="100" Height="40" Content="Click Me" />
Constraints allow flexibility while preventing extreme sizes.
<!-- Good: Flexible with constraints -->
<TextBox MinWidth="150" MaxWidth="400" />

<!-- Less flexible -->
<TextBox Width="200" />
Modern panels like StackPanel have Spacing properties that are cleaner than individual margins.
<!-- Good: Use panel spacing -->
<StackPanel Spacing="8">
    <Button Content="Button 1" />
    <Button Content="Button 2" />
</StackPanel>

<!-- More verbose -->
<StackPanel>
    <Button Content="Button 1" Margin="0,0,0,8" />
    <Button Content="Button 2" />
</StackPanel>

See Also

Build docs developers (and LLMs) love