Controls

Canvas

Canvas is the framework's free-positioning panel. It lays out each child at an explicit X/Y offset using the attached Canvas.Left and Canvas.Top properties, making it the right panel for overlays, HUDs, anchored popups, drag surfaces, and lightweight 2D scene composition.

Quick start

Use Canvas when child position is part of your UI state. Unlike flow panels such as StackPanel or Grid, Canvas does not compute placement from rows, columns, or stacking order.

Minimal positioned child

<Canvas Width="320" Height="180">
  <TextBlock Text="Marker"
             Canvas.Left="24"
             Canvas.Top="18" />
</Canvas>

Two independent children

<Canvas Width="420" Height="220">
  <Button Text="Primary action"
          Width="140"
          Height="40"
          Canvas.Left="20"
          Canvas.Top="20" />

  <Border Background="#1C2C3B"
          BorderBrush="#50708F"
          BorderThickness="1"
          Padding="8"
          Canvas.Left="210"
          Canvas.Top="96">
    <TextBlock Text="Pinned note" />
  </Border>
</Canvas>

Set coordinates from code

var badge = new Label
{
    Text = "Paused"
};

Canvas.SetLeft(badge, 16f);
Canvas.SetTop(badge, 12f);
overlayCanvas.AddChild(badge);

Default recommendation: choose Canvas only when explicit coordinates are the behavior you want to own. If the layout should adapt automatically to available space, start with Grid, StackPanel, or DockPanel instead.

How Canvas layout works

Canvas uses a deliberately simple layout contract. Every child is measured unconstrained, then arranged at its own explicit offset with its measured size.

Measure

Each child is measured with infinite available width and height. Canvas does not try to constrain children to its own available size during measure.

Arrange

Each child is arranged at Canvas.Left/Canvas.Top relative to the canvas origin, using the child's desired size as its arranged width and height.

  • If Canvas.Left is not set, the child uses 0 for X.
  • If Canvas.Top is not set, the child uses 0 for Y.
  • Canvas desired width is the maximum of left + child desired width across all children.
  • Canvas desired height is the maximum of top + child desired height across all children.
  • Negative coordinates are allowed and can place content partially or fully outside the visible canvas region.
<Canvas>
  <TextBlock Text="Alpha"
             Canvas.Left="10"
             Canvas.Top="12" />
  <TextBlock Text="Beta"
             Canvas.Left="120"
             Canvas.Top="40" />
</Canvas>

If Alpha wants 60x20 and Beta wants 50x20, the Canvas desired size becomes at least 170x60.

Positioning API

The current Canvas implementation exposes exactly two positioning attached properties: Canvas.Left and Canvas.Top.

XAML syntax

Set coordinates directly on the child element with attached-property syntax such as Canvas.Left="48" and Canvas.Top="12".

Code API

Use Canvas.SetLeft, Canvas.SetTop, Canvas.GetLeft, and Canvas.GetTop from code-behind or runtime logic.

Position a control in XAML

<Canvas Width="500" Height="280">
  <Button x:Name="SaveButton"
          Text="Save"
          Width="120"
          Height="38"
          Canvas.Left="32"
          Canvas.Top="24" />
</Canvas>

Read and update coordinates at runtime

var currentLeft = Canvas.GetLeft(saveButton);
var currentTop = Canvas.GetTop(saveButton);

Canvas.SetLeft(saveButton, currentLeft + 12f);
Canvas.SetTop(saveButton, currentTop + 8f);

Animate a positioned element

var leftAnimation = new DoubleAnimation
{
    To = 320f,
    Duration = TimeSpan.FromMilliseconds(250)
};

Storyboard.SetTarget(leftAnimation, runner);
Storyboard.SetTargetProperty(leftAnimation, "(Canvas.Left)");

Important: the framework does not currently implement Canvas.Right or Canvas.Bottom. Document and design against Left and Top only.

Sizing and clipping behavior

Canvas is free-positioning, not free-visibility. Children can be arranged anywhere, but rendering and hit testing are clipped to the canvas layout slot.

Unconstrained measure

Children measure against infinite size, so text, borders, and controls can request their natural size even when the canvas itself is smaller.

Viewport clip

Canvas always clips to its own layout slot. Content outside the visible rectangle is not drawn and is not hit-test visible through the clip chain.

Final size

Canvas returns the final size given by its parent during arrange. Its own children do not force the final arranged size to expand at that stage.

<Border Width="220" Height="120" BorderBrush="#3A556F" BorderThickness="1">
  <Canvas Width="220" Height="120">
    <TextBlock Text="Visible"
               Canvas.Left="12"
               Canvas.Top="12" />
    <TextBlock Text="Partially clipped"
               Canvas.Left="170"
               Canvas.Top="92" />
  </Canvas>
</Border>

That second child may still be arranged at the requested coordinates, but any pixels outside the 220x120 canvas viewport are clipped away.

Common usage patterns

HUD or overlay layer

<Canvas Width="1280" Height="720">
  <Border Background="#AA101820"
          BorderBrush="#50708F"
          BorderThickness="1"
          Padding="10,6"
          Canvas.Left="24"
          Canvas.Top="24">
    <TextBlock Text="Mission timer: 02:18" />
  </Border>

  <Label Text="Inventory"
         Canvas.Left="1110"
         Canvas.Top="24" />
</Canvas>

Freeform editor surface

<Canvas Width="900" Height="540">
  <Border Background="#182636"
          BorderBrush="#4FC7C1"
          BorderThickness="1"
          Padding="12"
          Width="220"
          Height="120"
          Canvas.Left="80"
          Canvas.Top="60">
    <TextBlock Text="Node A" />
  </Border>

  <Border Background="#182636"
          BorderBrush="#4FC7C1"
          BorderThickness="1"
          Padding="12"
          Width="220"
          Height="120"
          Canvas.Left="420"
          Canvas.Top="220">
    <TextBlock Text="Node B" />
  </Border>
</Canvas>

Popup anchor host

<Canvas Width="640" Height="360">
  <Button x:Name="AnchorButton"
          Text="Open menu"
          Width="140"
          Height="38"
          Canvas.Left="40"
          Canvas.Top="30" />

  <Popup IsOpen="True"
         PlacementTarget="{Binding ElementName=AnchorButton}" />
</Canvas>

Mixed content dashboard

<Canvas Width="760" Height="420">
  <TextBlock Text="Status" Canvas.Left="20" Canvas.Top="16" />
  <ProgressBar Width="240" Height="20" Value="68" Canvas.Left="20" Canvas.Top="44" />
  <Button Text="Retry" Width="96" Height="34" Canvas.Left="20" Canvas.Top="90" />
  <Border Width="320" Height="180" BorderBrush="#45627E" BorderThickness="1" Canvas.Left="380" Canvas.Top="20">
    <TextBlock Text="Preview area" Margin="12" />
  </Border>
</Canvas>

Canvas vs other panels

Choose Canvas when

Position comes from coordinates, gameplay state, diagram state, manual dragging, or anchor math that you control directly.

Choose another panel when

You want rows, columns, stacking, docking, wrapping, or automatic redistribution based on available size.

  • Use Grid for forms, dashboards, and structured two-dimensional layout.
  • Use StackPanel for linear vertical or horizontal flows.
  • Use DockPanel for edge-based composition.
  • Use Canvas for explicit coordinates and overlay composition.

Property reference

Canvas attached properties

Canvas.Left: float X offset from the canvas origin. Unset maps to 0 during layout.

Canvas.Top: float Y offset from the canvas origin. Unset maps to 0 during layout.

Inherited and common properties

Width and Height: useful when you want a fixed visible drawing region.

UseLayoutRounding: inherited from the framework element stack and affects rounded arranged positions when enabled.

Notes and pitfalls

Only Left and Top exist today. Do not promise Right, Bottom, or WPF parity beyond the currently implemented attached properties.

Canvas does not auto-layout children. If two children overlap, that is expected. Canvas will not resolve collisions, spacing, or stacking for you.

Measure is unconstrained. Large or wrapping content can request more space than the visible canvas region. If that is not what you want, constrain the child explicitly with Width, Height, or a different parent panel.

Use coordinates for placement, not margin. Margin may still affect the child's own layout behavior, but Canvas positioning is driven by Canvas.Left and Canvas.Top.

Content outside the viewport is clipped. Negative coordinates and oversized content are valid, but only the portion inside the canvas layout slot is visible and hit-testable.