Blazor Pie Chart
A BlazorExpress.ChartJS pie chart component is a circular chart that shows the proportional values of different categories.
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.
<PieChart @ref="pieChart" 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 PieChart pieChart = default!;
private PieChartOptions pieChartOptions = 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) };
pieChartOptions = new();
pieChartOptions.Responsive = true;
pieChartOptions.Plugins.Title!.Text = "2022 - Sales";
pieChartOptions.Plugins.Title.Display = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await pieChart.InitializeAsync(chartData, pieChartOptions);
}
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 PieChartDataset pieChartDataset
&& pieChartDataset is not null
&& pieChartDataset.Data is not null)
{
var count = pieChartDataset.Data.Count;
var newData = new List<double?>();
for (var i = 0; i < count; i++)
{
newData.Add(random.Next(0, 100));
}
pieChartDataset.Data = newData;
newDatasets.Add(pieChartDataset);
}
}
chartData.Datasets = newDatasets;
await pieChart.UpdateAsync(chartData, pieChartOptions);
}
private async Task AddDatasetAsync()
{
if (chartData is null || chartData.Datasets is null) return;
var chartDataset = GetRandomPieChartDataset();
chartData = await pieChart.AddDatasetAsync(chartData, chartDataset, pieChartOptions);
}
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 PieChartDataset pieChartDataset)
data.Add(new PieChartDatasetData(pieChartDataset.Label, random.Next(0, 100), backgroundColors![dataLabelsCount]));
}
chartData = await pieChart.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(GetRandomPieChartDataset());
}
return datasets;
}
private PieChartDataset GetRandomPieChartDataset()
{
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 #
<PieChart @ref="pieChart" 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 PieChart pieChart = default!;
private PieChartOptions pieChartOptions = 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) };
pieChartOptions = new();
pieChartOptions.Responsive = true;
pieChartOptions.Plugins.Title!.Text = "2022 - Sales";
pieChartOptions.Plugins.Title.Display = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// pass the plugin name to enable the data labels
await pieChart.InitializeAsync(chartData: chartData, chartOptions: pieChartOptions, 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 PieChartDataset pieChartDataset
&& pieChartDataset is not null
&& pieChartDataset.Data is not null)
{
var count = pieChartDataset.Data.Count;
var newData = new List<double?>();
for (var i = 0; i < count; i++)
{
newData.Add(random.Next(0, 100));
}
pieChartDataset.Data = newData;
newDatasets.Add(pieChartDataset);
}
}
chartData.Datasets = newDatasets;
await pieChart.UpdateAsync(chartData, pieChartOptions);
}
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 PieChartDataset pieChartDataset)
data.Add(new PieChartDatasetData(pieChartDataset.Label, random.Next(0, 100), backgroundColors![dataLabelsCount]));
}
chartData = await pieChart.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 = GetRandomPieChartDataset();
if (index == 0)
dataset.Datalabels.Anchor = Anchor.End;
else if (index == numberOfDatasets - 1)
dataset.Datalabels.Anchor = Anchor.End;
else
dataset.Datalabels.Anchor = Anchor.Center;
datasets.Add(dataset);
}
return datasets;
}
private PieChartDataset GetRandomPieChartDataset()
{
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
}Change legend position #
This sample demonstrates how to change the position of the chart legend.
<PieChart @ref="pieChart" Width="500" />
<div class="mt-5">
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="PositionTopAsync"> Position: top </Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="PositionRightAsync"> Position: right </Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="PositionBottomAsync"> Position: bottom </Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="PositionLeftAsync"> Position: left </Button>
</div>
@code {
private PieChart pieChart = default!;
private PieChartOptions pieChartOptions = 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) };
pieChartOptions = new();
pieChartOptions.Responsive = true;
pieChartOptions.Plugins.Title!.Text = "2022 - Sales";
pieChartOptions.Plugins.Title.Display = true;
pieChartOptions.Plugins.Legend.Position = "right";
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await pieChart.InitializeAsync(chartData, pieChartOptions);
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task PositionTopAsync() => await UpdatePositionAsync("top");
private async Task PositionRightAsync() => await UpdatePositionAsync("right");
private async Task PositionBottomAsync() => await UpdatePositionAsync("bottom");
private async Task PositionLeftAsync() => await UpdatePositionAsync("left");
private async Task UpdatePositionAsync(string position)
{
pieChartOptions.Plugins.Legend.Position = position;
await pieChart.UpdateAsync(chartData, pieChartOptions);
}
#region Data Preparation
private List<IChartDataset> GetDefaultDataSets(int numberOfDatasets)
{
var datasets = new List<IChartDataset>();
for (var index = 0; index < numberOfDatasets; index++)
{
datasets.Add(GetRandomPieChartDataset());
}
return datasets;
}
private PieChartDataset GetRandomPieChartDataset()
{
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
}Click event #
Set the
OnClick callback to react when a pie slice is selected. The callback receives ChartClickEventArgs with the dataset, item index, label, and raw value.
<PieChart @ref="pieChart" Width="600" OnClick="HandleClickAsync" />
@if (selectedChartItem is not null)
{
<p class="mt-3 mb-0">
Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex),
@FormatLabel(selectedChartItem) (index @selectedChartItem.Index),
value @FormatValue(selectedChartItem)
</p>
}
@code {
private PieChart pieChart = default!;
private PieChartOptions pieChartOptions = default!;
private ChartData chartData = default!;
private ChartClickEventArgs? selectedChartItem;
protected override void OnInitialized()
{
chartData = new ChartData
{
Labels = new List<string> { "Desktop", "Tablet", "Mobile" },
Datasets = new List<IChartDataset>
{
new PieChartDataset
{
Label = "Sessions",
Data = new List<double?> { 62, 18, 20 },
BackgroundColor = new List<string> { "#4BC0C0", "#9966FF", "#FF9F40" },
},
},
};
pieChartOptions = new PieChartOptions { Responsive = true };
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await pieChart.InitializeAsync(chartData, pieChartOptions);
await base.OnAfterRenderAsync(firstRender);
}
private Task HandleClickAsync(ChartClickEventArgs eventArgs)
{
selectedChartItem = eventArgs;
return Task.CompletedTask;
}
private static string FormatLabel(ChartClickEventArgs eventArgs) => eventArgs.Label ?? $"item {eventArgs.Index}";
private static string FormatValue(ChartClickEventArgs eventArgs) => eventArgs.Value?.ToString() ?? "null";
}Chart Components — Dedicated Library
Chart components have been moved to the dedicated BlazorExpress.ChartJS library to support independent feature additions and easier maintenance. Complete the following steps in the Blazor project that renders the charts.
- Install the package used by these demos:
dotnet add package BlazorExpress.ChartJS --version 1.2.4 - Import the component namespace in
_Imports.razor:@using BlazorExpress.ChartJS - Load the browser scripts in this order from the host page, after the Blazor framework script.
The data-labels plugin is needed only when a chart uses data labels.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js"></script> <script src="_content/BlazorExpress.ChartJS/blazorexpress.chartjs.js"></script>
See the BlazorExpress.ChartJS website and GitHub repository for current package documentation. We also recommend checking the original BlazorExpress.ChartJS demos when adapting advanced examples.
For a complete charts setup walkthrough, see the
Getting Started — Charts Setup section.