Skip to main content
Avalonia provides a comprehensive drawing API through the DrawingContext class and related drawing primitives. The drawing system supports shapes, text, images, and custom rendering.

DrawingContext

The primary interface for drawing operations. Obtained from Render method overrides or custom draw operations.

Drawing Methods

DrawLine
void
Draws a line between two points.
public override void Render(DrawingContext context)
{
    var pen = new Pen(Brushes.Black, 2);
    context.DrawLine(pen, new Point(0, 0), new Point(100, 100));
}
Parameters:
  • pen - The pen defining stroke color and thickness
  • p1 - Start point
  • p2 - End point
DrawRectangle
void
Draws a rectangle.
context.DrawRectangle(
    brush: Brushes.LightBlue,
    pen: new Pen(Brushes.Navy, 1),
    rect: new Rect(10, 10, 100, 50),
    radiusX: 5,  // Optional corner radius
    radiusY: 5
);
DrawEllipse
void
Draws an ellipse.
context.DrawEllipse(
    brush: Brushes.Red,
    pen: new Pen(Brushes.DarkRed, 2),
    center: new Point(50, 50),
    radiusX: 40,
    radiusY: 30
);
DrawGeometry
void
Draws a geometry (complex shape).
var geometry = StreamGeometry.Parse("M 0,0 L 100,0 L 50,100 Z");
context.DrawGeometry(
    brush: Brushes.Green,
    pen: new Pen(Brushes.DarkGreen, 1),
    geometry: geometry
);
DrawImage
void
Draws an image.
// Draw entire image
context.DrawImage(image, new Rect(0, 0, 100, 100));

// Draw part of image
context.DrawImage(
    source: image,
    sourceRect: new Rect(0, 0, 50, 50),    // Source area
    destRect: new Rect(10, 10, 100, 100)   // Destination area
);
DrawText
void
Draws formatted text. Note: Use FormattedText or TextLayout for advanced text rendering.
var text = new FormattedText(
    "Hello, World!",
    CultureInfo.CurrentCulture,
    FlowDirection.LeftToRight,
    new Typeface("Arial"),
    24,
    Brushes.Black
);
context.DrawText(text, new Point(10, 10));

Transform Methods

PushTransform
IDisposable
Applies a transformation matrix. Returns a disposable that restores the previous transform.
using (context.PushTransform(Matrix.CreateRotation(Math.PI / 4)))
{
    // Drawing operations here are rotated 45 degrees
    context.DrawRectangle(Brushes.Blue, null, new Rect(0, 0, 50, 50));
}
// Transform automatically restored
PushOpacity
IDisposable
Applies an opacity level to subsequent drawing operations.
using (context.PushOpacity(0.5))
{
    // Drawing operations here are 50% transparent
    context.DrawEllipse(Brushes.Red, null, new Point(50, 50), 40, 40);
}
PushClip
IDisposable
Clips subsequent drawing to a rectangular region.
using (context.PushClip(new Rect(0, 0, 100, 100)))
{
    // Only the part within the rectangle will be visible
    context.DrawEllipse(Brushes.Blue, null, new Point(150, 50), 80, 80);
}
PushGeometryClip
IDisposable
Clips subsequent drawing to a geometry shape.
var clipGeometry = new EllipseGeometry(new Rect(0, 0, 100, 100));
using (context.PushGeometryClip(clipGeometry))
{
    // Drawing is clipped to ellipse shape
    context.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 150, 150));
}

Pen

Defines how lines and shape outlines are drawn.

Properties

Brush
IBrush?
The brush used for the stroke color.
Thickness
double
The width of the stroke in device-independent pixels.
DashStyle
IDashStyle?
The dash pattern for the stroke (solid, dashed, dotted, etc.).
LineCap
PenLineCap
The shape at the start and end of lines. Values: Flat, Round, Square.
LineJoin
PenLineJoin
How line segments join. Values: Miter, Bevel, Round.
MiterLimit
double
The limit on the ratio of the miter length to half the thickness.

Usage Examples

// Solid pen
var solidPen = new Pen(Brushes.Black, 2);

// Dashed pen
var dashedPen = new Pen(Brushes.Blue, 1)
{
    DashStyle = DashStyle.Dash
};

// Custom dash pattern
var customPen = new Pen(Brushes.Red, 3)
{
    DashStyle = new DashStyle(new[] { 2.0, 1.0 }, 0), // 2 on, 1 off
    LineCap = PenLineCap.Round,
    LineJoin = PenLineJoin.Round
};

Geometry Classes

Define complex shapes for drawing.

StreamGeometry

Efficient geometry created from path data.
var geometry = StreamGeometry.Parse("M 0,0 L 100,0 L 100,100 L 0,100 Z");
context.DrawGeometry(Brushes.Yellow, new Pen(Brushes.Black), geometry);

// Or build programmatically
var streamGeometry = new StreamGeometry();
using (var ctx = streamGeometry.Open())
{
    ctx.BeginFigure(new Point(0, 0), isFilled: true);
    ctx.LineTo(new Point(100, 0));
    ctx.LineTo(new Point(100, 100));
    ctx.LineTo(new Point(0, 100));
    ctx.EndFigure(isClosed: true);
}

Built-in Geometries

// Rectangle
var rect = new RectangleGeometry(new Rect(0, 0, 100, 50));

// Ellipse
var ellipse = new EllipseGeometry(new Rect(0, 0, 100, 100));

// Line
var line = new LineGeometry(new Point(0, 0), new Point(100, 100));

// Polyline
var polyline = new PolylineGeometry(
    new[] { new Point(0, 0), new Point(50, 100), new Point(100, 0) },
    isFilled: false
);

// Combined geometries
var combined = new GeometryGroup()
{
    Children = new GeometryCollection { rect, ellipse },
    FillRule = FillRule.EvenOdd
};

Drawing Class

Represents a drawing that can be rendered.

GeometryDrawing

Combines a geometry with a brush and/or pen.
var drawing = new GeometryDrawing
{
    Geometry = new EllipseGeometry(new Rect(0, 0, 100, 100)),
    Brush = Brushes.LightBlue,
    Pen = new Pen(Brushes.Navy, 2)
};

// Use in DrawingImage
var image = new DrawingImage(drawing);

DrawingGroup

Groups multiple drawings together.
var group = new DrawingGroup
{
    Children = new DrawingCollection
    {
        new GeometryDrawing { /* ... */ },
        new ImageDrawing { /* ... */ },
        new GlyphRunDrawing { /* ... */ }
    },
    Opacity = 0.8,
    Transform = new RotateTransform(45)
};

Complete Drawing Examples

Custom Control with Drawing

public class CustomShapeControl : Control
{
    public override void Render(DrawingContext context)
    {
        // Fill background
        context.DrawRectangle(
            Brushes.White, 
            null, 
            new Rect(Bounds.Size)
        );
        
        // Draw border
        var borderPen = new Pen(Brushes.Gray, 1);
        context.DrawRectangle(
            null, 
            borderPen, 
            new Rect(Bounds.Size)
        );
        
        // Draw centered circle
        var center = new Point(Bounds.Width / 2, Bounds.Height / 2);
        var radius = Math.Min(Bounds.Width, Bounds.Height) / 3;
        
        context.DrawEllipse(
            Brushes.CornflowerBlue,
            new Pen(Brushes.Navy, 2),
            center,
            radius,
            radius
        );
        
        base.Render(context);
    }
}

Drawing with Transformations

public override void Render(DrawingContext context)
{
    var center = new Point(Bounds.Width / 2, Bounds.Height / 2);
    
    // Draw rotated rectangles
    for (int i = 0; i < 8; i++)
    {
        var angle = i * Math.PI / 4;
        var matrix = Matrix.CreateTranslation(-center)
            .Append(Matrix.CreateRotation(angle))
            .Append(Matrix.CreateTranslation(center));
        
        using (context.PushTransform(matrix))
        {
            var rect = new Rect(
                center.X - 40,
                center.Y - 10,
                80,
                20
            );
            
            context.DrawRectangle(
                Brushes.Orange,
                new Pen(Brushes.DarkOrange, 1),
                rect
            );
        }
    }
}

Drawing Complex Paths

public override void Render(DrawingContext context)
{
    var geometry = new StreamGeometry();
    
    using (var ctx = geometry.Open())
    {
        // Draw a star
        var points = new Point[10];
        var centerX = Bounds.Width / 2;
        var centerY = Bounds.Height / 2;
        var outerRadius = Math.Min(Bounds.Width, Bounds.Height) / 2 - 10;
        var innerRadius = outerRadius * 0.4;
        
        for (int i = 0; i < 10; i++)
        {
            var angle = i * Math.PI / 5 - Math.PI / 2;
            var radius = i % 2 == 0 ? outerRadius : innerRadius;
            points[i] = new Point(
                centerX + radius * Math.Cos(angle),
                centerY + radius * Math.Sin(angle)
            );
        }
        
        ctx.BeginFigure(points[0], isFilled: true);
        for (int i = 1; i < points.Length; i++)
        {
            ctx.LineTo(points[i]);
        }
        ctx.EndFigure(isClosed: true);
    }
    
    var gradient = new LinearGradientBrush
    {
        StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
        EndPoint = new RelativePoint(1, 1, RelativeUnit.Relative),
        GradientStops =
        {
            new GradientStop { Color = Colors.Gold, Offset = 0 },
            new GradientStop { Color = Colors.Orange, Offset = 1 }
        }
    };
    
    context.DrawGeometry(gradient, new Pen(Brushes.DarkGoldenrod, 2), geometry);
}

Drawing with Clipping

public override void Render(DrawingContext context)
{
    var clipRect = new Rect(10, 10, Bounds.Width - 20, Bounds.Height - 20);
    
    using (context.PushClip(clipRect))
    {
        // Draw pattern that extends beyond clip region
        for (int x = 0; x < Bounds.Width; x += 20)
        {
            for (int y = 0; y < Bounds.Height; y += 20)
            {
                context.DrawEllipse(
                    Brushes.LightBlue,
                    null,
                    new Point(x, y),
                    8,
                    8
                );
            }
        }
    }
    
    // Draw clip boundary
    context.DrawRectangle(
        null,
        new Pen(Brushes.Red, 2),
        clipRect
    );
}

Best Practices

  1. Use Push Methods: Always use using statements with Push* methods to ensure state is restored
  2. Minimize DrawingContext Calls: Batch drawing operations when possible
  3. Cache Geometries: Reuse Geometry objects rather than creating new ones each frame
  4. Use StreamGeometry: For complex paths, StreamGeometry is more efficient than PathGeometry
  5. Invalidate Efficiently: Only call InvalidateVisual() when necessary
  6. Consider Performance: Complex geometries and many draw calls can impact performance
  7. Use Immutable Brushes: Predefined brushes from Brushes class are immutable and cached
  8. Dispose Resources: Dispose Pen instances that use custom brushes
  9. Test DPI Scaling: Ensure drawing looks correct at different DPI settings

Build docs developers (and LLMs) love