Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Blazor

Drawing with the HTML Canvas Element in Blazor Server-side

3.48/5 (8 votes)
3 Jun 2020CPOL5 min read 49K  
The HTML Canvas element allows for Drawing. This article describes how to use it with Blazor Server-side.
This article is part of a series of articles. This article describes how to create a Blazor Server-side app in Visual Studio 2019 and use a canvas element to draw on. The article presumes a basic understanding of C#, HTML, CSS and Razor markup syntax. By the end of the article, you will know how to draw on the HTML Canvas element in Blazor Server-side style in C#.

Introduction

Blazor Server-side is an open-source technology built on top of .NET Core which supports Windows, Linux and Apple OS's (Operating Systems). You can write all your code in C# which is my language of choice. You can use whatever language the .NET Core CLR supports.

The HTML <canvas> element allows you to draw via manipulating it via JavaScript functions.

Combing the two leads to a Web Page in which you can draw on the canvas element on the client browser.

You can draw whatever you want. For a look at what all is possible to draw on the <canvas> element in JavaScript, look at the following article:

The GitHub Repository is at:

The other articles in this series you can use depending on your needs are as published:

Requirements

Install Visual Studio 2019 Community Edition or use the Pro or Enterprise edition if you or your employer gives it to you. You will need to have the Web Development section checked during install time of VS 2019 for Blazor Server-side solution template.

You will need to know basic C#.

Create the Default Blazor Server-Side Solution (for Beginners)

Open Visual Studio 2019 and you should click the Create New Project button.

Image 1

Then type Blazor in the search box. You should see the Blazor App Template. Make sure it's the one for C#. Then select it and click the Next button at the bottom right.

Image 2

Then give it a project name. I have chosen to name mine BlazorCanvasApp. Give the Location to where you want to put your solution files at on your drive. You can leave it to the default location if you like.

Do not type the location as in the example as this is my own drives folder path which will not be present on your computer.

Then click the Create button on the bottom right corner.

Image 3

Then select Blazor Server App. This will make a Blazor Server-side App. Click the Create button in the bottom right corner.

Image 4

Then the Blazor Solution will be created. It has some default code in it. So just hit the Play button and let it build and compile and run and it will invoke in your default browser.

Image 5

My default browser is Google Chrome on Windows 10 Pro. This is the result as shown in the figure below:

Image 6

Now Add the Nuget Packages to the Solution (for Beginners)

From the menu in Visual Studio 2019, choose menu option Tools => NuGet Package Manager => Manage NuGet Packages for Solution...

This will pop up the window where we will add the Blazor.Extensions.Canvas NuGet Package to our project.

Now make sure you are in the Browse tab. Then in the search box, type Blazor.Extensions.Canvas. Click and select Blazor.Extensions.Canvas to select the NuGet Package. Then on the right side, click the checkbox to select it for the project. Then click the Install button. Wait for the NuGet Package to finish installing.

Image 7

In the same manner, add the Newtonsoft.Json NuGet Package. We will be using this to do JavaScript Interop. JSInterop is a method by which we can call from C# code a JavaScript function.

Code the Canvas Drawing Razor Component (for Everyone)

In the right pane inside Visual Studio, go to Solution Explorer pane. Expand the folder node Pages. Right-click it and from the pop-up context menu, select menu option Add => Razor Component...

From the pop-up modal window, give a name to the component. I have given CanvasDrawing.razor. Then click the Add button.

Image 8

In this file, paste the following code:

C#
@page "/CanvasDrawing"
@using Blazor.Extensions
@using Blazor.Extensions.Canvas
@using Blazor.Extensions.Canvas.Canvas2D
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq 
@inject IJSRuntime jsRuntime

<div @ref="divCanvas" @onclick="OnClick">
    <BECanvas @ref="myCanvas" Height="800" Width="800"></BECanvas>
</div>

<h3>CanvasDrawing</h3>

@code {
    ElementReference divCanvas;
    Blazor.Extensions.BECanvasComponent myCanvas;
    Canvas2DContext currentCanvasContext;

    class PieChartSlice
    {
        public string Name { get; set; }
        public double PercentageOfSlice { get; set; }
        public string ColorOfSlice { get; set; }
        public double LabelX { get; set; }
        public double LabelY { get; set; }
    }

    async void OnClick(MouseEventArgs eventArgs)
    {
        double mouseX = 0;
        double mouseY = 0;
        List<PieChartSlice> pieChartData = new List<PieChartSlice>();

        if (divCanvas.Id?.Length > 0)
        {
            string data = await jsRuntime.InvokeAsync<string>("getDivCanvasOffsets",
                new object[] { divCanvas });
            JObject offsets = (JObject)JsonConvert.DeserializeObject(data);
            mouseX = eventArgs.ClientX - offsets.Value<double>("offsetLeft");
            mouseY = eventArgs.ClientY - offsets.Value<double>("offsetTop");

            currentCanvasContext = await myCanvas.CreateCanvas2DAsync();

            await currentCanvasContext.ClearRectAsync(0, 0, 800, 800);
            await currentCanvasContext.SetFillStyleAsync("Red");
            await currentCanvasContext.FillRectAsync(mouseX, mouseY, 5, 5);
            await currentCanvasContext.StrokeTextAsync("ClientX: " + mouseX +
                "   Client Y: " + mouseY, 20, 20);

            pieChartData.Add(new PieChartSlice { 
                Name = "Akshay's Company", 
                PercentageOfSlice = 50, 
                ColorOfSlice = "Blue", 
                LabelX = 287, 
                LabelY = 459 });
            pieChartData.Add(new PieChartSlice { 
                Name = "John Doe's Company", 
                PercentageOfSlice = 30, 
                ColorOfSlice = "Red", 
                LabelX = 200, 
                LabelY = 260 });
            pieChartData.Add(new PieChartSlice {
                Name = "Jane Doe's Company", 
                PercentageOfSlice = 20, 
                ColorOfSlice = "Green", 
                LabelX = 400, 
                LabelY = 280 });

            if (myCanvas != null && currentCanvasContext != null)
            {
                await currentCanvasContext.SaveAsync();
                await currentCanvasContext.SetFillStyleAsync("Black");
                await currentCanvasContext.SetFontAsync("15pt Ariel");
                await currentCanvasContext.FillTextAsync(
                    "Widget Industry Market Share Breakup %", 160, 120);
                await currentCanvasContext.SetFontAsync("12pt Ariel");
                await currentCanvasContext.SetStrokeStyleAsync("Black");
                double currangle = 0;
                double lastangle = 0;
                for (var i = 0; i < pieChartData.Count; i++)
                {
                    currangle += (pieChartData[i].PercentageOfSlice * 360) / 100;
                    await currentCanvasContext.SetFillStyleAsync(
                        pieChartData[i].ColorOfSlice);
                    await currentCanvasContext.BeginPathAsync();
                    await currentCanvasContext.MoveToAsync(350, 350);
                    await currentCanvasContext.ArcAsync(350, 350, 200, 
                        (Math.PI / 180) * lastangle, 
                        (Math.PI / 180) * currangle, false);
                    await currentCanvasContext.ClosePathAsync();
                    await currentCanvasContext.FillAsync();
                    await currentCanvasContext.StrokeTextAsync(pieChartData[i].Name, 
                        pieChartData[i].LabelX, pieChartData[i].LabelY);
                    lastangle = currangle;
                }
                await currentCanvasContext.RestoreAsync();
            }
        }
    }
}

The @page directive says that the URL will be the base URL + /CanvasDrawing. The @using is the same as C# using statements. The @inject directive puts the IJSRuntime into the Razor component. This allows us to do JSInterop. We can use the jsRuntime variable of type IJSRuntime to call our JavaScript function which will return the offsetLeft and offsetTop property values for the <div>. These are then subtracted from the ClientX and ClientY coming from the mouse click event arguments to get the actual position of the mouse within the <canvas> element. The @ref in the <div> element creates an ElementReference. This is passed to the JavaScript function to get the offsets. BECanvas is the Blazor.Extensions.Canvas which creates a <canvas> element. The @ref allows us to get a reference to the <canvas> element to create the 2D context with which we will draw on the <canvas> element.

Why am I wrapping the Canvas element in a Div? This is because as of the writing of the article, the OnClick event is not available for the Canvas element. There are many caveats like this as Blazor is a new technology.

Now we need to create the JavaScript function getDivCanvasOffsets in the _Host.cshtml file. So open this file and at the end before the last closing tag for body, paste in the following code:

JavaScript
<script src="_content/Blazor.Extensions.Canvas/blazor.extensions.canvas.js"></script>
<script>
    function getDivCanvasOffsets(el) {
        var obj = {};
        obj.offsetLeft = el.offsetLeft;
        obj.offsetTop = el.offsetTop;
        return JSON.stringify(obj);
    }
</script>

The first script is for the Blazor.Extensions.NuGet Package. This contains all the JavaScript functions which the NuGet package uses JSInterop to invoke to control the <canvas> element.

Now you can run the project in Visual Studio and you will get to the normal page in the browser. You need to change the URL to point at the route for the razor component which is in my case localhost:4046/CanvasDrawing. You can click on the canvas and it will draw the coordinates at which you clicked on the top left-hand corner of the canvas. At the point you clicked, it will draw a small 5x5 pixel filled rectangle in red colour.

An example of the output follows:

Image 9

If you can draw some text and a rectangle in Red, you can draw anything.

I would suggest first learning the whole Canvas API in JavaScript. Then realising that if there is a MoveTo(x,y) in JavaScript API for Canvas element, then its equivalent in Blazor.Extensions.Canvas is MoveToAsync(x,y). Of course, all drawing functions are in the Canvas 2D Context which is derived from the Canvas element. The pattern follows the HTML <canvas> elements JavaScript API. There are some things like Gradients that are not available with Blazor.Extensions.Canvas. You have to watch out for what is available.

History

  • 2nd June, 2020 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)