mathcanvasmauidraw

How to draw a pie in .NET MAUI


I need to create a progress bar that looks like this.

enter image description here

What I did for now is:

public void Draw(ICanvas canvas, RectF dirtyRect)
{
    float effectiveSize = Size - Thickness;
    float x = Thickness / 2;
    float y = Thickness / 2;

    if (Progress < 0)
    {
        Progress = 0;
    }
    else if (Progress > 100)
    {
        Progress = 100;
    }

    if (Progress < 100)
    {
        float angle = GetAngle(Progress);

        canvas.StrokeColor = ProgressLeftColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);

        // Draw arc
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawArc(x, y, effectiveSize, effectiveSize, 90, angle, true, false);
    }
    else
    {
        // Draw circle
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);
    }

    float fontSize = effectiveSize / 2.86f;
    canvas.FontSize = fontSize;
    canvas.FontColor = TextColor;

    float verticalPosition = ((Size / 2) - (fontSize / 2)) * 1.15f;
    canvas.DrawString($"{Progress}%", x, verticalPosition, effectiveSize, effectiveSize / 4, HorizontalAlignment.Center, VerticalAlignment.Center);
}

This draws a nice circle with an arc. I am struggling with the inner pie part (light gray part). I need that part to be filled with some color.

One of my ideas was to create two lines then connect them with an arc, and fill the object. Something like this.

canvas.FillColor = Colors.Teal;

var center = new Point(effectiveSize / 2, effectiveSize / 2);
var startPoint = new Point(x, y);
var endPoint = new Point();
canvas.DrawLine(center, startPoint);
canvas.DrawLine(center, endPoint);
canvas.DrawArc((float)startPoint.X, (float)startPoint.Y,float)endPoint.X, (float)endPoint.Y, 0, angle, true, false);

How can I calculate the endpoint. Or is there any better solution.

thnx

enter image description here


Solution

  • You may try using SkiaSharp to draw this pie. I share a solution here. For more info, you may refer to SkiaSharp Curves and Paths. It also works for maui.

    Use a slider to control the angle:

    <VerticalStackLayout
        Spacing="25"
        Padding="30,0"
        VerticalOptions="Center">
    
        
        <Slider Maximum="360" ValueChanged="Slider_ValueChanged" />
    
        <skia:SKCanvasView x:Name="mycanvas" WidthRequest="400" HeightRequest="400" PaintSurface="SKCanvasView_PaintSurface"/>
    
    
    </VerticalStackLayout>
    

    In code behind:

    void Slider_ValueChanged(System.Object sender, Microsoft.Maui.Controls.ValueChangedEventArgs e)
    {
        if (e.NewValue >= 360)
        {
            // the value cannnot be equal to 360
            SweepAngle = (float)359.99;
        }
        else
        {
            SweepAngle = (float)e.NewValue;
        }
        mycanvas.InvalidateSurface();
    }
    
    void SKCanvasView_PaintSurface(System.Object sender, SkiaSharp.Views.Maui.SKPaintSurfaceEventArgs e)
    {
        SKImageInfo info = e.Info;
        SKSurface surface = e.Surface;
        SKCanvas canvas = surface.Canvas;
    
        canvas.Clear();
    
    
        // draw the default background circle ring
        SKPaint cPaint = new SKPaint
        {
            Style = SKPaintStyle.Stroke,
            Color = SKColors.LightGray,
            StrokeWidth = 50
        };
        SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
        canvas.DrawCircle(center, 200, cPaint);
    
        cPaint.Color = SKColors.AliceBlue;
        cPaint.Style = SKPaintStyle.Fill;
        canvas.DrawCircle(center, 200, cPaint);
    
    
        //draw the progress bar outer ring
        SKRect rect = new SKRect(center.X-200, center.Y-200, center.X + 200, center.Y + 200);
    
        SKPaint aPaint = new SKPaint()
        {
    
            StrokeWidth = 50,
            Color = SKColors.Green,
            IsStroke = true
        };
    
        canvas.DrawArc(rect, -90, SweepAngle, false,aPaint);
        
        // draw the inner circle of the progress bar
        using (SKPath path = new SKPath())
        using (SKPaint fillPaint = new SKPaint())
        using (SKPaint outlinePaint = new SKPaint())
        {
            SKRect rect1 = new SKRect(center.X - 200, center.Y - 200, center.X + 200, center.Y + 200);
    
            path.MoveTo(e.Info.Width / 2, e.Info.Height / 2);
            path.ArcTo(rect1, -90, SweepAngle, false);
    
            fillPaint.Style = SKPaintStyle.Fill;
            fillPaint.Color = SKColors.LightGreen;
    
            canvas.DrawPath(path, fillPaint);
        }
        SKPaint textPaint = new SKPaint
        {
            Color = SKColors.LightSeaGreen,
            StrokeWidth = 1,
        };
    
        // draw the text
        string text = (Math.Round(SweepAngle/360*100,0)).ToString() + "%";
        float textWidth = textPaint.MeasureText(text);
        textPaint.TextSize = 0.2f * info.Width * textPaint.TextSize / textWidth;
    
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);
    
        float xText = info.Width / 2 - textBounds.MidX;
        float yText = info.Height / 2 - textBounds.MidY;
    
        canvas.DrawText(text, xText, yText, textPaint);
    }
    

    enter image description here

    Hope it helps!