Blazor Doughnut Chart
A Blazor donut chart component is a circular chart that shows the proportional values of different categories. It is similar to a pie chart, but the center of the donut chart is hollow. This makes it easier to see the individual values of each category.
Prerequisites #
Refer to the getting started guide for setting up charts.
How it works #
In the following example, a categorical 12-color palette is used.
TIP
For data visualization, you can use the predefined palettes
ColorUtility.CategoricalTwelveColors
for a 12-color palette and ColorUtility.CategoricalSixColors
for a 6-color palette.
These palettes offer a range of distinct and visually appealing colors that can be applied to represent different categories or data elements in your visualizations.
<DoughnutChart @ref="doughnutChart" Width="500" />
<div class="mt-5">
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await RandomizeAsync()"> Randomize </Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await AddDatasetAsync()"> Add Dataset </Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await AddDataAsync()">Add Data</Button>
</div>
@code {
private DoughnutChart doughnutChart = default!;
private DoughnutChartOptions doughnutChartOptions = default!;
private ChartData chartData = default!;
private string[]? backgroundColors;
private int datasetsCount = 0;
private int dataLabelsCount = 0;
private Random random = new();
protected override void OnInitialized()
{
backgroundColors = ColorUtility.CategoricalTwelveColors;
chartData = new ChartData { Labels = GetDefaultDataLabels(4), Datasets = GetDefaultDataSets(1) };
doughnutChartOptions = new();
doughnutChartOptions.Responsive = true;
doughnutChartOptions.Plugins.Title!.Text = "2022 - Sales";
doughnutChartOptions.Plugins.Title.Display = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await doughnutChart.InitializeAsync(chartData, doughnutChartOptions);
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task RandomizeAsync()
{
if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return;
var newDatasets = new List<IChartDataset>();
foreach (var dataset in chartData.Datasets)
{
if (dataset is DoughnutChartDataset doughnutChartDataset
&& doughnutChartDataset is not null
&& doughnutChartDataset.Data is not null)
{
var count = doughnutChartDataset.Data.Count;
var newData = new List<double?>();
for (var i = 0; i < count; i++)
{
newData.Add(random.Next(0, 100));
}
doughnutChartDataset.Data = newData;
newDatasets.Add(doughnutChartDataset);
}
}
chartData.Datasets = newDatasets;
await doughnutChart.UpdateAsync(chartData, doughnutChartOptions);
}
private async Task AddDatasetAsync()
{
if (chartData is null || chartData.Datasets is null) return;
var chartDataset = GetRandomDoughnutChartDataset();
chartData = await doughnutChart.AddDatasetAsync(chartData, chartDataset, doughnutChartOptions);
}
private async Task AddDataAsync()
{
if (dataLabelsCount >= 12)
return;
if (chartData is null || chartData.Datasets is null)
return;
var data = new List<IChartDatasetData>();
foreach (var dataset in chartData.Datasets)
{
if (dataset is DoughnutChartDataset doughnutChartDataset)
{
data.Add(new DoughnutChartDatasetData(doughnutChartDataset.Label, random.Next(0, 100), backgroundColors![dataLabelsCount]));
}
}
chartData = await doughnutChart.AddDataAsync(chartData, GetNextDataLabel(), data);
dataLabelsCount += 1;
}
#region Data Preparation
private List<IChartDataset> GetDefaultDataSets(int numberOfDatasets)
{
var datasets = new List<IChartDataset>();
for (var index = 0; index < numberOfDatasets; index++)
{
datasets.Add(GetRandomDoughnutChartDataset());
}
return datasets;
}
private DoughnutChartDataset GetRandomDoughnutChartDataset()
{
datasetsCount += 1;
return new() { Label = $"Team {datasetsCount}", Data = GetRandomData(), BackgroundColor = GetRandomBackgroundColors() };
}
private List<double?> GetRandomData()
{
var data = new List<double?>();
for (var index = 0; index < dataLabelsCount; index++)
{
data.Add(random.Next(0, 100));
}
return data;
}
private List<string> GetRandomBackgroundColors()
{
var colors = new List<string>();
for (var index = 0; index < dataLabelsCount; index++)
{
colors.Add(backgroundColors![index]);
}
return colors;
}
private List<string> GetDefaultDataLabels(int numberOfLabels)
{
var labels = new List<string>();
for (var index = 0; index < numberOfLabels; index++)
{
labels.Add(GetNextDataLabel());
dataLabelsCount += 1;
}
return labels;
}
private string GetNextDataLabel() => $"Product {dataLabelsCount + 1}";
private string GetNextDataBackgrounfColor() => backgroundColors![dataLabelsCount];
#endregion Data Preparation
}
Data labels #
<DoughnutChart @ref="doughnutChart" Width="500" />
<div class="mt-5">
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await RandomizeAsync()"> Randomize </Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await AddDataAsync()">Add Data</Button>
</div>
@code {
private DoughnutChart doughnutChart = default!;
private DoughnutChartOptions doughnutChartOptions = default!;
private ChartData chartData = default!;
private string[]? backgroundColors;
private int datasetsCount = 0;
private int dataLabelsCount = 0;
private Random random = new();
protected override void OnInitialized()
{
backgroundColors = ColorUtility.CategoricalTwelveColors;
chartData = new ChartData { Labels = GetDefaultDataLabels(4), Datasets = GetDefaultDataSets(3) };
doughnutChartOptions = new();
doughnutChartOptions.Responsive = true;
doughnutChartOptions.Plugins.Title!.Text = "2022 - Sales";
doughnutChartOptions.Plugins.Title.Display = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// pass the plugin name to enable the data labels
await doughnutChart.InitializeAsync(chartData: chartData, chartOptions: doughnutChartOptions, plugins: new string[] { "ChartDataLabels" });
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task RandomizeAsync()
{
if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return;
var newDatasets = new List<IChartDataset>();
foreach (var dataset in chartData.Datasets)
{
if (dataset is DoughnutChartDataset doughnutChartDataset
&& doughnutChartDataset is not null
&& doughnutChartDataset.Data is not null)
{
var count = doughnutChartDataset.Data.Count;
var newData = new List<double?>();
for (var i = 0; i < count; i++)
{
newData.Add(random.Next(0, 100));
}
doughnutChartDataset.Data = newData;
newDatasets.Add(doughnutChartDataset);
}
}
chartData.Datasets = newDatasets;
await doughnutChart.UpdateAsync(chartData: chartData, chartOptions: doughnutChartOptions);
}
private async Task AddDataAsync()
{
if (dataLabelsCount >= 12)
return;
if (chartData is null || chartData.Datasets is null)
return;
var data = new List<IChartDatasetData>();
foreach (var dataset in chartData.Datasets)
{
if (dataset is DoughnutChartDataset doughnutChartDataset)
{
data.Add(new DoughnutChartDatasetData(doughnutChartDataset.Label, random.Next(0, 100), backgroundColors![dataLabelsCount]));
}
}
chartData = await doughnutChart.AddDataAsync(chartData, GetNextDataLabel(), data);
dataLabelsCount += 1;
}
#region Data Preparation
private List<IChartDataset> GetDefaultDataSets(int numberOfDatasets)
{
var datasets = new List<IChartDataset>();
for (var index = 0; index < numberOfDatasets; index++)
{
var dataset = GetRandomDoughnutChartDataset();
if (index == 0)
dataset.Datalabels.Anchor = Anchor.End;
else if (index == numberOfDatasets - 1)
dataset.Datalabels.Anchor = Anchor.Start;
else
dataset.Datalabels.Anchor = Anchor.Center;
datasets.Add(dataset);
}
return datasets;
}
private DoughnutChartDataset GetRandomDoughnutChartDataset()
{
datasetsCount += 1;
return new() { Label = $"Team {datasetsCount}", Data = GetRandomData(), BackgroundColor = GetRandomBackgroundColors() };
}
private List<double?> GetRandomData()
{
var data = new List<double?>();
for (var index = 0; index < dataLabelsCount; index++)
{
data.Add(random.Next(0, 100));
}
return data;
}
private List<string> GetRandomBackgroundColors()
{
var colors = new List<string>();
for (var index = 0; index < dataLabelsCount; index++)
{
colors.Add(backgroundColors![index]);
}
return colors;
}
private List<string> GetDefaultDataLabels(int numberOfLabels)
{
var labels = new List<string>();
for (var index = 0; index < numberOfLabels; index++)
{
labels.Add(GetNextDataLabel());
dataLabelsCount += 1;
}
return labels;
}
private string GetNextDataLabel() => $"Product {dataLabelsCount + 1}";
private string GetNextDataBackgrounfColor() => backgroundColors![dataLabelsCount];
#endregion Data Preparation
}