Search code examples
c#avaloniauiavalonia

How to draw on a canvas in avalonia?


I am learning Avalonia for a small project and I really enjoy it, but I can't find much documentation on drawing in a canvas.

My goal is very simple: to draw a grid on my canvas...

However, every time I try, nothing shows up. Even though the lines are being called when I place breakpoints, I am a bit lost.

I created this example to illustrate:

public partial class Tests : UserControl, IInterface
{

    private double gridSize = 2;
    private SolidColorBrush gridBrush = new SolidColorBrush(Colors.Black);

    public Tests()
    {
        InitializeComponent();
        gridCanvas.InvalidateVisual();
    }

    public override void Render(DrawingContext context)
    {
        base.Render(context);
        DrawGrid(context);
    }

    private void DrawGrid(DrawingContext context)
    {
        var canvasWidth = gridCanvas.Bounds.Width;
        var canvasHeight = gridCanvas.Bounds.Height;

        var pen = new Pen(Brushes.Black, thickness: 20); // Adjust thickness if necessary

        for (double x = 0; x < canvasWidth; x += gridSize)
        {
            context.DrawLine(pen, new Point(x, 0), new Point(x, canvasHeight));
        }

        for (double y = 0; y < canvasHeight; y += gridSize)
        {
            context.DrawLine(pen, new Point(0, y), new Point(canvasWidth, y));
        }
    }
}
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
             x:Class="AvaloniaTest.Tests">

    <Canvas Name="gridCanvas" Background="Beige">
        <Rectangle Fill="Red" Canvas.Left="10" Canvas.Top="10" Width="5" Height="5"/>
    </Canvas>
    
</UserControl>

In this example, I can see my red rectangle in the top left corner and the beige background, but not the grid. Can someone explain why?

Thanks in advance, and have a nice day.


Solution

  • As mentioned by Jeroen van Langen, your problem is you are drawing to the UserControl drawing context. You could use Lines and add them to the Canvas.

    Line line = new()
    {
      // Setup.
    };
    gridCanvas.Children.Add(line);
    

    However, Be aware that Line is a Shape (Avalonia.Controls.Shapes). Shapes are controls, that is they inherit Control. This means they have a lot of overhead you may not need in order to allow them to function as a control. This is OK if you need control functionality, such as the ability to move them around dynamically, but if all you need is a static drawing and especially if you would otherwise need a large number of shapes, this can be resource costly.

    However, you can render a DrawingContext to a Control (or any visual where the Render isn't sealed). Unfortunately we don't have a DrawingVisual.RenderOpen option like WPF to grab a DrawingContext. Instead you will have to create a class that derives from Control and override Render on that object.

    public class DrawingCanvas : Control
    {
      // Constructors and stuff
    
      public override void Render(DrawingContext context)
      {
        base.Render(context);
        // Draw here.
      }
    }
    

    You may also need to call InvalidateVisual() to render.

    To include this on your view, just add a local namespace:

    xmlns:local="clr-namespace=AvaloniaTest"
    

    And preface the name in xaml:

    <local:DrawingCanvas/>