Draw graphical objects

Browse sample. Browse the sample

.NET Multi-platform App UI (.NET MAUI) graphics, in the Microsoft.Maui.Graphics namespace, enables you to draw graphical objects on a canvas that's defined as an ICanvas object.

The .NET MAUI GraphicsView control provides access to an ICanvas object, on which properties can be set and methods invoked to draw graphical objects. For more information about the GraphicsView, see GraphicsView.

Note

Many of the graphical objects have Draw and Fill methods, for example DrawRectangle and FillRectangle. A Draw method draws the outline of the shape, which is unfilled. A Fill method draws the outline of the shape and also fills it.

Graphical objects are drawn on an ICanvas using a device-independent unit that's recognized by each platform. This ensures that graphical objects are scaled appropriately to the pixel density of the underlying platform.

Draw a line

Lines can be drawn on an ICanvas using the DrawLine method, which requires four float arguments that represent the start and end points of the line.

The following example shows how to draw a line:

canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 6;
canvas.DrawLine(10, 10, 90, 100);

In this example, a red diagonal line is drawn from (10,10) to (90,100):

Screenshot of a red line.

Note

There's also a DrawLine overload that takes two PointF arguments.

The following example shows how to draw a dashed line:

canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.StrokeDashPattern = new float[] { 2, 2 };
canvas.DrawLine(10, 10, 90, 100);

In this example, a red dashed diagonal line is drawn from (10,10) to (90,100):

Screenshot of a dashed red line.

For more information about dashed lines, see Draw dashed objects.

Draw an ellipse

Ellipses and circles can be drawn on an ICanvas using the DrawEllipse method, which requires x, y, width, and height arguments, of type float.

The following example shows how to draw an ellipse:

canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.DrawEllipse(10, 10, 100, 50);

In this example, a red ellipse with dimensions 100x50 is drawn at (10,10):

Screenshot of a red ellipse.

To draw a circle, make the width and height arguments to the DrawEllipse method equal:

canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.DrawEllipse(10, 10, 100, 100);

In this example, a red circle with dimensions 100x100 is drawn at (10,10):

Screenshot of a red circle.

Note

Circles can also be drawn with the DrawCircle method.

For information about drawing a dashed ellipse, see Draw dashed objects.

A filled ellipse can be drawn with the FillEllipse method, which also requires x, y, width, and height arguments, of type float:

canvas.FillColor = Colors.Red;
canvas.FillEllipse(10, 10, 150, 50);

In this example, a red filled ellipse with dimensions 150x50 is drawn at (10,10):

Screenshot of a red filled ellipse.

The FillColor property of the ICanvas object must be set to a Color before invoking the FillEllipse method.

Filled circles can also be drawn with the FillCircle method.

Note

There are DrawEllipse and FillEllipse overloads that take Rect and RectF arguments. In addition, there are also DrawCircle and FillCircle overloads.

Draw a rectangle

Rectangles and squares can be drawn on an ICanvas using the DrawRectangle method, which requires x, y, width, and height arguments, of type float.

The following example shows how to draw a rectangle:

canvas.StrokeColor = Colors.DarkBlue;
canvas.StrokeSize = 4;
canvas.DrawRectangle(10, 10, 100, 50);

In this example, a dark blue rectangle with dimensions 100x50 is drawn at (10,10):

Screenshot of a dark blue rectangle.

To draw a square, make the width and height arguments to the DrawRectangle method equal:

canvas.StrokeColor = Colors.DarkBlue;
canvas.StrokeSize = 4;
canvas.DrawRectangle(10, 10, 100, 100);

In this example, a dark blue square with dimensions 100x100 is drawn at (10,10):

Screenshot of a dark blue square.

For information about drawing a dashed rectangle, see Draw dashed objects.

A filled rectangle can be drawn with the FillRectangle method, which also requires x, y, width, and height arguments, of type float:

canvas.FillColor = Colors.DarkBlue;
canvas.FillRectangle(10, 10, 100, 50);

In this example, a dark blue filled rectangle with dimensions 100x50 is drawn at (10,10):

Screenshot of a dark blue filled rectangle.

The FillColor property of the ICanvas object must be set to a Color before invoking the FillRectangle method.

Note

There are DrawRectangle and FillRectangle overloads that take Rect and RectF arguments.

Draw a rounded rectangle

Rounded rectangles and squares can be drawn on an ICanvas using the DrawRoundedRectangle method, which requires x, y, width, height, and cornerRadius arguments, of type float. The cornerRadius argument specifies the radius used to round the corners of the rectangle.

The following example shows how to draw a rounded rectangle:

canvas.StrokeColor = Colors.Green;
canvas.StrokeSize = 4;
canvas.DrawRoundedRectangle(10, 10, 100, 50, 12);

In this example, a green rectangle with rounded corners and dimensions 100x50 is drawn at (10,10):

Screenshot of a green rounded rectangle.

For information about drawing a dashed rounded rectangle, see Draw dashed objects.

A filled rounded rectangle can be drawn with the FillRoundedRectangle method, which also requires x, y, width, height, and cornerRadius arguments, of type float:

canvas.FillColor = Colors.Green;
canvas.FillRoundedRectangle(10, 10, 100, 50, 12);

In this example, a green filled rectangle with rounded corners and dimensions 100x50 is drawn at (10,10):

Screenshot of a green filled rounded rectangle.

The FillColor property of the ICanvas object must be set to a Color before invoking the FillRoundedRectangle method.

Note

There are DrawRoundedRectangle and FillRoundedRectangle overloads that take Rect and RectF arguments, and overloads that enable the radius of each corner to be separately specified.

Draw an arc

Arcs can be drawn on an ICanvas using the DrawArc method, which requires x, y, width, height, startAngle, and endAngle arguments of type float, and clockwise and closed arguments of type bool. The startAngle argument specifies the angle from the x-axis to the starting point of the arc. The endAngle argument specifies the angle from the x-axis to the end point of the arc. The clockwise argument specifies the direction in which the arc is drawn, and the closed argument specifies whether the end point of the arc will be connected to the start point.

The following example shows how to draw an arc:

canvas.StrokeColor = Colors.Teal;
canvas.StrokeSize = 4;
canvas.DrawArc(10, 10, 100, 100, 0, 180, true, false);

In this example, a teal arc of dimensions 100x100 is drawn at (10,10). The arc is drawn in a clockwise direction from 0 degrees to 180 degrees, and isn't closed:

Screenshot of a teal arc.

For information about drawing a dashed arc, see Draw dashed objects.

A filled arc can be drawn with the FillArc method, which requires x, y, width, height, startAngle, and endAngle arguments of type float, and a clockwise argument of type bool:

canvas.FillColor = Colors.Teal;
canvas.FillArc(10, 10, 100, 100, 0, 180, true);

In this example, a filled teal arc of dimensions 100x100 is drawn at (10,10). The arc is drawn in a clockwise direction from 0 degrees to 180 degrees, and is closed automatically:

Screenshot of a filled teal arc.

The FillColor property of the ICanvas object must be set to a Color before invoking the FillArc method.

Note

There are DrawArc and FillArc overloads that take Rect and RectF arguments.

Draw a path

A path is a collection of one or more contours. Each contour is a collection of connected straight lines and curves. Contours are not connected to each other but they might visually overlap. Sometimes a single contour can overlap itself.

Paths are used to draw curves and complex shapes and can be drawn on an ICanvas using the DrawPath method, which requires a PathF argument.

A contour generally begins with a call to the PathF.MoveTo method, which you can express either as a PointF value or as separate x and y coordinates. The MoveTo call establishes a point at the beginning of the contour and an initial current point. You can then call the following methods to continue the contour with a line or curve from the current point to a point specified in the method, which then becomes the new current point:

  • LineTo to add a straight line to the path.
  • AddArc to add an arc, which is a line on the circumference of a circle or ellipse.
  • CurveTo to add a cubic Bezier spline.
  • QuadTo to add a quadratic Bezier spline.

None of these methods contain all of the data necessary to describe the line or curve. Instead, each method works with the current point established by the method call immediately preceding it. For example, the LineTo method adds a straight line to the contour based on the current point.

A contour ends with another call to MoveTo, which begins a new contour, or a call to Close, which closes the contour. The Close method automatically appends a straight line from the current point to the first point of the contour, and marks the path as closed.

The PathF class also defines other methods and properties. The following methods add entire contours to the path:

The following example shows how to draw a path:

PathF path = new PathF();
path.MoveTo(40, 10);
path.LineTo(70, 80);
path.LineTo(10, 50);
path.Close();
canvas.StrokeColor = Colors.Green;
canvas.StrokeSize = 6;
canvas.DrawPath(path);

In this example, a closed green triangle is drawn:

Screenshot of a closed green triangle.

A filled path can be drawn with the FillPath, which also requires a PathF argument:

PathF path = new PathF();
path.MoveTo(40, 10);
path.LineTo(70, 80);
path.LineTo(10, 50);
canvas.FillColor = Colors.SlateBlue;
canvas.FillPath(path);

In this example, a filled slate blue triangle is drawn:

Screenshot of a filled slate blue triangle.

The FillColor property of the ICanvas object must be set to a Color before invoking the FillPath method.

Important

The FillPath method has an overload that enables a WindingMode to be specified, which sets the fill algorithm that's used. For more information, see Winding modes.

Draw an image

Images can be drawn on an ICanvas using the DrawImage method, which requires an IImage argument, and x, y, width, and height arguments, of type float.

The following example shows how to load an image and draw it to the canvas:

using System.Reflection;
using IImage = Microsoft.Maui.Graphics.IImage;
#if IOS || ANDROID || MACCATALYST
using Microsoft.Maui.Graphics.Platform;
#elif WINDOWS
using Microsoft.Maui.Graphics.Win2D;
#endif

IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
#if IOS || ANDROID || MACCATALYST
    // PlatformImage isn't currently supported on Windows.
    image = PlatformImage.FromStream(stream);
#elif WINDOWS
    image = new W2DImageLoadingService().FromStream(stream);
#endif
}

if (image != null)
{
    canvas.DrawImage(image, 10, 10, image.Width, image.Height);
}
using System.Reflection;
using IImage = Microsoft.Maui.Graphics.IImage;
using Microsoft.Maui.Graphics.Platform;

IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
    image = PlatformImage.FromStream(stream);
}

if (image != null)
{
    canvas.DrawImage(image, 10, 10, image.Width, image.Height);
}

In this example, an image is retrieved from the assembly and loaded as a stream. It's then drawn at actual size at (10,10):

Screenshot of an image.

Important

Loading an image that's embedded in an assembly requires the image to have its build action set to Embedded Resource rather than MauiImage.

Draw a string

Strings can be drawn on an ICanvas using one of the DrawString overloads. The appearance of each string can be defined by setting the Font, FontColor, and FontSize properties. String alignment can be specified by horizontal and vertical alignment options that perform alignment within the string's bounding box.

Note

The bounding box for a string is defined by its x, y, width, and height arguments.

The following examples show how to draw strings:

canvas.FontColor = Colors.Blue;
canvas.FontSize = 18;

canvas.Font = Font.Default;
canvas.DrawString("Text is left aligned.", 20, 20, 380, 100, HorizontalAlignment.Left, VerticalAlignment.Top);
canvas.DrawString("Text is centered.", 20, 60, 380, 100, HorizontalAlignment.Center, VerticalAlignment.Top);
canvas.DrawString("Text is right aligned.", 20, 100, 380, 100, HorizontalAlignment.Right, VerticalAlignment.Top);

canvas.Font = Font.DefaultBold;
canvas.DrawString("This text is displayed using the bold system font.", 20, 140, 350, 100, HorizontalAlignment.Left, VerticalAlignment.Top);

canvas.Font = new Font("Arial");
canvas.FontColor = Colors.Black;
canvas.SetShadow(new SizeF(6, 6), 4, Colors.Gray);
canvas.DrawString("This text has a shadow.", 20, 200, 300, 100, HorizontalAlignment.Left, VerticalAlignment.Top);

In this example, strings with different appearance and alignment options are displayed:

Screenshot of strings using different alignment options.

Note

The DrawString overloads also enable truncation and line spacing to be specified.

For information about drawing shadows, see Draw a shadow.

Draw attributed text

Attributed text can be drawn on an ICanvas using the DrawText method, which requires an IAttributedText argument, and x, y, width, and height arguments, of type float. Attributed text is a string with associated attributes for parts of its text, that typically represents styling data.

The following example shows how to draw attributed text:

using Microsoft.Maui.Graphics.Text;
...

canvas.Font = new Font("Arial");
canvas.FontSize = 18;
canvas.FontColor = Colors.Blue;

string markdownText = @"This is *italic text*, **bold text**, __underline text__, and ***bold italic text***.";
IAttributedText attributedText = MarkdownAttributedTextReader.Read(markdownText); // Requires the Microsoft.Maui.Graphics.Text.Markdig package
canvas.DrawText(attributedText, 10, 10, 400, 400);

In this example, markdown is converted to attributed text and displayed with the correct styling:

Screenshot of correctly rendered markdown.

Important

Drawing attributed text requires you to have added the Microsoft.Maui.Graphics.Text.Markdig NuGet package to your project.

Draw with fill and stroke

Graphical objects with both fill and stroke can be drawn to the canvas by calling a draw method after a fill method. For example, to draw an outlined rectangle, set the FillColor and StrokeColor properties to colors, then call the FillRectangle method followed by the DrawRectangle method.

The following example draws a filled circle, with a stroke outline, as a path:

float radius = Math.Min(dirtyRect.Width, dirtyRect.Height) / 4;

PathF path = new PathF();
path.AppendCircle(dirtyRect.Center.X, dirtyRect.Center.Y, radius);

canvas.StrokeColor = Colors.Blue;
canvas.StrokeSize = 10;
canvas.FillColor = Colors.Red;

canvas.FillPath(path);
canvas.DrawPath(path);

In this example, the stroke and fill colors for a PathF object are specified. The filled circle is drawn, then the outline stroke of the circle:

Screenshot of a circle drawn with fill and stroke.

Warning

Calling a draw method before a fill method will result in an incorrect z-order. The fill will be drawn over the stroke, and the stroke won't be visible.

Draw a shadow

Graphical objects drawn on an ICanvas can have a shadow applied using the SetShadow method, which takes the following arguments:

  • offset, of type SizeF, specifies an offset for the shadow, which represents the position of a light source that creates the shadow.
  • blur, of type float, represents the amount of blur to apply to the shadow.
  • color, of type Color, defines the color of the shadow.

The following examples show how to add shadows to filled objects:

canvas.FillColor = Colors.Red;
canvas.SetShadow(new SizeF(10, 10), 4, Colors.Grey);
canvas.FillRectangle(10, 10, 90, 100);

canvas.FillColor = Colors.Green;
canvas.SetShadow(new SizeF(10, -10), 4, Colors.Grey);
canvas.FillEllipse(110, 10, 90, 100);

canvas.FillColor = Colors.Blue;
canvas.SetShadow(new SizeF(-10, 10), 4, Colors.Grey);
canvas.FillRoundedRectangle(210, 10, 90, 100, 25);

In these examples, shadows whose light sources are in different positions are added to the filled objects, with identical amounts of blur:

Screenshot of a objects drawn with shadows.

Draw dashed objects

ICanvas objects have a StrokeDashPattern property, of type float[]. This property is an array of float values that indicate the pattern of dashes and gaps that are to be used when drawing the stroke for an object. Each float in the array specifies the length of a dash or gap. The first item in the array specifies the length of a dash, while the second item in the array specifies the length of a gap. Therefore, float values with an even index value specify dashes, while float values with an odd index value specify gaps.

The following example shows how to draw a dashed square, using a regular dash:

canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.StrokeDashPattern = new float[] { 2, 2 };
canvas.DrawRectangle(10, 10, 90, 100);

In this example, a square with a regular dashed stroke is drawn:

Screenshot of a regular dashed square.

The following example shows how to draw a dashed square, using an irregular dash:

canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.StrokeDashPattern = new float[] { 4, 4, 1, 4 };
canvas.DrawRectangle(10, 10, 90, 100);

In this example, a square with an irregular dashed stroke is drawn:

Screenshot of an irregular dashed square.

Control line ends

A line has three parts: start cap, line body, and end cap. The start and end caps describe the start and end of a line.

ICanvas objects have a StrokeLineCap property, of type LineCap, that describes the start and end of a line. The LineCap enumeration defines the following members:

  • Butt, which represents a line with a square end, drawn to extend to the exact endpoint of the line. This is the default value of the StrokeLineCap property.
  • Round, which represents a line with a rounded end.
  • Square, which represents a line with a square end, drawn to extend beyond the endpoint to a distance equal to half the line width.

The following example shows how to set the StrokeLineCap property:

canvas.StrokeSize = 10;
canvas.StrokeColor = Colors.Red;
canvas.StrokeLineCap = LineCap.Round;
canvas.DrawLine(10, 10, 110, 110);

In this example, the red line is rounded at the start and end of the line:

Screenshot of three lines with different line caps.

Control line joins

ICanvas objects have a StrokeLineJoin property, of type LineJoin, that specifies the type of join that is used at the vertices of an object. The LineJoin enumeration defines the following members:

  • Miter, which represents angular vertices that produce a sharp or clipped corner. This is the default value of the StrokeLineJoin property.
  • Round, which represents rounded vertices that produce a circular arc at the corner.
  • Bevel, which represents beveled vertices that produce a diagonal corner.

Note

When the StrokeLineJoin property is set to Miter, the MiterLimit property can be set to a float to limit the miter length of line joins in the object.

The following example shows how to set the StrokeLineJoin property:

PathF path = new PathF();
path.MoveTo(10, 10);
path.LineTo(110, 50);
path.LineTo(10, 110);

canvas.StrokeSize = 20;
canvas.StrokeColor = Colors.Blue;
canvas.StrokeLineJoin = LineJoin.Round;
canvas.DrawPath(path);

In this example, the blue PathF object has rounded joins at its vertices:

Screenshot of the effect of the three different LineJoin enumeration members.

Clip objects

Graphical objects that are drawn to an ICanvas can be clipped prior to drawing, with the following methods:

  • ClipPath clips an object so that only the area that's within the region of a PathF object will be visible.
  • ClipRectangle clips an object so that only the area that's within the region of a rectangle will be visible. The rectangle can be specified using float arguments, or by a Rect or RectF argument.
  • SubtractFromClip clips an object so that only the area that's outside the region of a rectangle will be visible. The rectangle can be specified using float arguments, or by a Rect or RectF argument.

The following example shows how to use the ClipPath method to clip an image:

using System.Reflection;
using IImage = Microsoft.Maui.Graphics.IImage;
#if IOS || ANDROID || MACCATALYST
using Microsoft.Maui.Graphics.Platform;
#elif WINDOWS
using Microsoft.Maui.Graphics.Win2D;
#endif

IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
#if IOS || ANDROID || MACCATALYST
    // PlatformImage isn't currently supported on Windows.
    image = PlatformImage.FromStream(stream);
#elif WINDOWS
    image = new W2DImageLoadingService().FromStream(stream);
#endif
}

if (image != null)
{
    PathF path = new PathF();
    path.AppendCircle(100, 90, 80);
    canvas.ClipPath(path);  // Must be called before DrawImage
    canvas.DrawImage(image, 10, 10, image.Width, image.Height);
}
using System.Reflection;
using IImage = Microsoft.Maui.Graphics.IImage;
using Microsoft.Maui.Graphics.Platform;

IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
    image = PlatformImage.FromStream(stream);
}

if (image != null)
{
    PathF path = new PathF();
    path.AppendCircle(100, 90, 80);
    canvas.ClipPath(path);  // Must be called before DrawImage
    canvas.DrawImage(image, 10, 10, image.Width, image.Height);
}

In this example, the image is clipped using a PathF object that defines a circle that's centered at (100,90) with a radius of 80. The result is that only the part of the image within the circle is visible:

Screenshot of an image that's been clipped with the ClipPath method.

Important

The ClipPath method has an overload that enables a WindingMode to be specified, which sets the fill algorithm that's used when clipping. For more information, see Winding modes.

The following example shows how to use the SubtractFromClip method to clip an image:

using System.Reflection;
using IImage = Microsoft.Maui.Graphics.IImage;
#if IOS || ANDROID || MACCATALYST
using Microsoft.Maui.Graphics.Platform;
#elif WINDOWS
using Microsoft.Maui.Graphics.Win2D;
#endif

IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
#if IOS || ANDROID || MACCATALYST
    // PlatformImage isn't currently supported on Windows.
    image = PlatformImage.FromStream(stream);
#elif WINDOWS
    image = new W2DImageLoadingService().FromStream(stream);
#endif
}

if (image != null)
{
    canvas.SubtractFromClip(60, 60, 90, 90);
    canvas.DrawImage(image, 10, 10, image.Width, image.Height);
}
using System.Reflection;
using IImage = Microsoft.Maui.Graphics.IImage;
using Microsoft.Maui.Graphics.Platform;

IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
    image = PlatformImage.FromStream(stream);
}

if (image != null)
{
    canvas.SubtractFromClip(60, 60, 90, 90);
    canvas.DrawImage(image, 10, 10, image.Width, image.Height);
}

In this example, the area defined by the rectangle that's specified by the arguments supplied to the SubtractFromClip method is clipped from the image. The result is that only the parts of the image outside the rectangle are visible:

Screenshot of an image that's been clipped with the SubtractFromClip method.