Introduction
Charts are fast becoming an essential part of user interfaces as more and more software requires users to be able to view/visualize data. While the most obvious examples are financial and numerical data, apps in other fields such as health, fitness, transportation, blogging and even social media feature graphical means of displaying data.
There have, consequently, been an increase in the number and quality of charting software and add-ons. Microsoft's Charts Helper was a relatively convenient means of creating graphs in ASP.NET applications; however, it is no longer the recommended means of creating charts.
Newer frontend libraries such as ChartsJS and Highcharts provide robust means of creating charts. They can be used with ASP.NET conveniently, with the data simply being supplied to these charting libraries on the frontend.
Using ChartsJS in .NET
With over 60k stars on GitHub and over 2 million npm downloads (as at the time of writing), ChartsJS is the most popular charting library for Javascript. It works with the HTML5 Canvas element, allowing data and configuration be passed in as regular Javascript.
Plus ChartJS has nice animations (which you can turn off if you don't want) that make the charts look and feel even nicer. 😁
Here's an example from the ChartsJS documentation:
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
The labels are supplied as an array of strings, the chart data values are also an array of numbers. The colours can also be provided so.
To use ChartsJS with our ASP.NET application simply requires us to provide the data from our backend to ChartsJS as arrays of values.
Demo
In this demo, available on GitHub at https://github.com/shegzee/dotnet-charts, I use ASP.NET's Razor pages to disiplay sample data in a bar chart and a pie chart.
Models/BankData.cs
In the data I will be charting, I include the bank name, colour for bar or pie slice and transactions count (the actual data value to be charted).
namespace TransChartDemo.Models
{
public class BankData
{
public int Id { get; set; }
public string Name { get; set; }
public string Colour { get; set; }
public int Year { get; set; }
public int TransactionsCount { get; set; }
}
}
Repositories/IBankDataRepository.cs
using TransChartDemo.Models;
namespace TransChartDemo.Repositories
{
public interface IBankDataRepository
{
BankData GetBankData(int id);
void AddBankData(BankData bankData);
void UpdateBankData(BankData bankData);
void DeleteBankData(int id);
List<BankData> GetAllBankData();
}
}
Repositories/BankDataRepository.cs
Here's the actual implementation of the IBankDataRepository
. I simply have a list of BankData
objects containing the relevant values.
using TransChartDemo.Models;
namespace TransChartDemo.Repositories
{
public class BankDataRepository : IBankDataRepository
{
List<BankData> sampleBankData = new List<BankData>
{
new BankData { Id = 1, Name = "GTB", Colour = "Orange", TransactionsCount = 1923 },
new BankData { Id = 2, Name = "UBA", Colour = "Red", TransactionsCount = 2011 },
new BankData { Id = 3, Name = "FBN", Colour = "LightBlue", TransactionsCount = 1820 },
new BankData { Id = 4, Name = "ZIB", Colour = "Black", TransactionsCount = 1500 },
new BankData { Id = 4, Name = "ACC", Colour = "LightGreen", TransactionsCount = 1947 }
};
public void AddBankData(BankData bankData)
{
sampleBankData.Add(bankData);
}
public void DeleteBankData(int id)
{
sampleBankData.Remove(sampleBankData.FirstOrDefault(b => b.Id == id));
}
public List<BankData> GetAllBankData()
{
return sampleBankData;
}
public BankData GetBankData(int id)
{
return sampleBankData.FirstOrDefault(b => b.Id == id);
}
public void UpdateBankData(BankData bankData)
{
var oldBankData = sampleBankData.FirstOrDefault();
if (oldBankData != null)
{
sampleBankData.Remove(oldBankData);
sampleBankData.Add(bankData);
}
}
}
}
Program.cs
In additional to the boilerplate, I configured Dependency Injection for IBankDataRepository.
using TransChartDemo.Repositories;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddScoped<IBankDataRepository, BankDataRepository>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Pages/ChartArrayBasic.cshtml.cs
In this file, I'm simply adding the data (the list of BankData objects I manually created in BankDataRepository.cs
) to the ViewData dictionary so it is accessible from ChartArrayBasic.cshtml. I went the route of using these almost redundant repository interface and implementation to demonstrate how easy it is to use ChartsJS with any ASP.NET application.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Text.Json;
using TransChartDemo.Repositories;
namespace TransChartDemo.Pages
{
public class ChartArrayBasicModel : PageModel
{
private readonly IBankDataRepository bankDataRepository;
public ChartArrayBasicModel(IBankDataRepository _bankDataRepository)
{
bankDataRepository = _bankDataRepository;
}
public void OnGet()
{
var bankData = bankDataRepository.GetAllBankData();
ViewData["allBankData"] = JsonSerializer.Serialize(bankData);
}
}
}
Here, the bank data is serialized as a JSON string and passed to the ViewData
dictionary in the line:
...
ViewData["allBankData"] = JsonSerializer.Serialize(bankData);
...
Pages/ChartArrayBasic.cshtml
This is the Razor Page containing the frontend.
@page
@model TransChartDemo.Pages.ChartArrayBasicModel
@{
}
<div class="text-center">
<h1 class="display-4">Charts.js Demo</h1>
<p>Integrating Charts.js in ASP.NET Core</a>.</p>
<div class="row">
<div class="col-sm-6">
<div class="card">
<div class="card-header">
Bar Chart
</div>
<div class="card-body">
<canvas id="bar" class="my-4 h-25"></canvas>
</div>
<div class="card-footer">
Transactions across top-tier banks
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card">
<div class="card-header">
Pie Chart
</div>
<div class="card-body">
<canvas id="pie" class="my-4 h-25"></canvas>
</div>
<div class="card-footer">
Transactions among top-tier banks ;-)
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script type="text/javascript">
var jsonData = '@Html.Raw(ViewData["allBankData"])';
var values = JSON.parse(jsonData);
//var arr = [];
// $.each(values, function (i, val) {
// var bankData = {};
// bankData.color = val.color;
// bankData.value = val.value;
// bankData.label = val.label;
// arr.push(bankData);
// });
var colours = {
'GTB': 'rgba(200, 100, 0, 0.5)',
'UBA': 'rgba(200, 50, 50, 0.5)',
'ZIB': 'rgba(100, 50, 50, 0.5)',
'FBN': 'rgba(50, 50, 200, 0.5)',
'ACC': 'rgba(50, 200, 100, 0.5)',
}
var barChartCanvas = document.getElementById('bar').getContext('2d');
//var barChart = new Chart(barChartCanvas).Bar(arr);
var barChart = new Chart(barChartCanvas, {
type: 'bar',
data: {
labels: values.map(x => x.Name),
datasets: [{
barThickness: 10,
label: 'Bar Chart',
data: values.map(x =>
x.TransactionsCount
),
backgroundColor: values.map(x =>
x.Colour
)
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
var pieChartCanvas = document.getElementById('pie').getContext('2d');
var pieChart = new Chart(pieChartCanvas, {
type: 'pie',
data: {
labels: values.map(x => x.Name),
datasets: [{
label: 'Pie Chart',
data: values.map(x =>
x.TransactionsCount
),
backgroundColor: values.map(x =>
colours[x.Name]
)
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
}
}
})
</script>
}
Here is the breakdown of this file:
In the main section of the page, we have two HTML5 Canvas elements.
<canvas id="bar" class="my-4 h-25"></canvas>
This canvas element is what will be used for the ChartsJS bar chart.
...
var barChartCanvas = document.getElementById('bar').getContext('2d');
var barChart = new Chart(barChartCanvas, {
type: 'bar',
data: {
...
...
<canvas id="pie" class="my-4 h-25"></canvas>
...
This element is used to render the ChartsJS pie chart.
...
var pieChartCanvas = document.getElementById('pie').getContext('2d');
var pieChart = new Chart(pieChartCanvas, {
type: 'pie',
data: {
...
The data passed in the ViewData
dictionary is output as a JSON string
...
var jsonData = '@Html.Raw(ViewData["allBankData"])';
...
It is then parsed as a Javascript object.
...
var values = JSON.parse(jsonData);
...
Next, I created a dictionary of colours (for use in the pie chart).
...
var colours = {
'GTB': 'rgba(200, 100, 0, 0.5)',
'UBA': 'rgba(200, 50, 50, 0.5)',
'ZIB': 'rgba(100, 50, 50, 0.5)',
'FBN': 'rgba(50, 50, 200, 0.5)',
'ACC': 'rgba(50, 200, 100, 0.5)',
}
...
Next, I created an instance of a Chart, configuring it as a bar chart.
...
var barChart = new Chart(barChartCanvas, {
type: 'bar',
data: {
...
Next, I supplied the labels, using the values sent from the JSON data.
...
labels: values.map(x => x.Name),
...
The labels are a list of the bank names.
Here is the complete section creating the bar chart:
...
var barChart = new Chart(barChartCanvas, {
type: 'bar',
data: {
labels: values.map(x => x.Name),
datasets: [{
barThickness: 10,
label: 'Bar Chart',
data: values.map(x =>
x.TransactionsCount
),
backgroundColor: values.map(x =>
x.Colour
)
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
...
The next property, datasets
contains the datasets. For a mixed chart, this property can contain multiple datasets of different types to be rendered together.
...
data: values.map(x =>
x.TransactionsCount
),
...
For the data
property of the dataset
, I pass an array containing values of the TransactionsCount
field in the bankData
list.
For the backgroundColor
property, I also passed an array from the bankData
list from the backend. I did something different for the pie chart.
Here is what the result looks like:
For the Pie chart, I did something similar.
...
var pieChartCanvas = document.getElementById('pie').getContext('2d');
var pieChart = new Chart(pieChartCanvas, {
type: 'pie',
data: {
labels: values.map(x => x.Name),
datasets: [{
label: 'Pie Chart',
data: values.map(x =>
x.TransactionsCount
),
backgroundColor: values.map(x =>
colours[x.Name]
)
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
}
}
})
...
The main difference, here, is that I used the colours dictionary I created earlier, using the bank names as keys, to populate the backgroundColor
property of the dataset.
Here is the result:
Conclusion
In this post, I detailed how I used ChartsJS to display data in a Razor page in ASP.NET.
Hopefully, this demonstrated how easily this can be done and how to go about it.
The code for this demo is available at https://github.com/shegzee/dotnet-charts.
I would appreciate feedback about this post, questions, comments and suggestions.
Thanks a lot for reading!
(This post was originally posted on LinkedIn)
Top comments (0)