Using Maui.Graphics to build charts
January 28, 2021
In this article you'll see how to implement a RadialBar chart that supports these two visualizations:
Grial UI Kit 4 comes with support for Xamarin.Forms and .NET MAUI, and since .NET MAUI provides an out-of-the box cross-platform 2D graphics canvas called Maui.Graphics, we are using it to implement our own Chart controls, removing the SkiaSharp dependency.
Enough context! Let’s take a look at the results of implementing one of the charts that MicroCharts has: RadialBar, in Maui.Graphics.
Putting pieces together
First things first. To use the drawing surface (the canvas) from Maui.Graphics you have to use a GraphicsView control. These controls have a Drawable property (of type IDrawable). Drawables are responsible for ultimately drawing your 2D stuff within the Draw() method that is called each time the GraphicsView gets invalidated.
I recommend you base your drawn controls classes on the GraphicsView and set the specific Drawable instance internally.
Any control should expose its properties as BindableProperties to support bindings, and it should refresh its state when a property changes. This can easily be achieved in this type of controls by calling Invalidate() within the property change callbacks of your custom properties.
In our chart we have:
- RadialBarChart: GraphicsView
- RadialBarChartDrawable: IDrawable
We also decided to pass the RadialBarChart instance to the drawable so it can read the BindableProperty values when drawing.
Drawing in the canvas
Drawing happens in the Draw method which receives the Maui.Graphics ICanvas instance.
Drawing code usually has two steps:
- setting style properties in the canvas, such as StrokeFill, FontSize, etc
- drawing with the primitives available such as DrawCircle, DrawString, etc, that will use the styles defined previously
Tip: you can save and restore the canvas styling state through canvas.SaveState() and canvas.RestoreState().
The code that draws each entry of the RadialBarChart does three things:
- Draw the background circle
- Draw the bars
- Draw the labels
In general you will want to draw the background elements first, so the foreground elements don’t get covered.
You can explore the code that draws the chart here.
Our goal was to have these two versions:
The main difference lies in how to draw the series labels. In the first example they are embedded in the drawing area so they must be drawn within the canvas to ensure they are well positioned. In the second example they are below the chart, but they could be placed in a completely separate area, adding extra design flexibility. For scenarios such as this, it’s better to draw the labels with XAML instead of Graphics. By doing this, you get to use all the power of the .NET MAUI layouts and styles that we know and love so much.
Let’s talk about the first scenario where everything is done within the canvas. The canvas has a very important method to measure text: GetStringSize(). Once you have the size of the text you are going to draw it’s easy to place it exactly where you want.
Note: use Math.Ceiling() to round up the size returned by GetStringSize() before drawing the text to make sure the text has enough space.
Our RadialChartControl has the property ShowLabels that, when set to true, renders the labels inside the canvas. Besides drawing the labels the angle total span is reduced from 360° to 270° to save the top-left quadrant for the labels.
You can explore the complete code in this repo.
To sum up
We are very pleased with the results. Maui.Graphics API is easy to understand - especially if you have experience with SkiaSharp, plus it’s super performant.
We’re soon to release line and bar charts that support multi-series, in Grial 4 for .NET MAUI.
Below a sneak peek of what to expect:
That’s all! Hope you found this useful! :)