Pie Chart with Custom Labels in WinForms

In this tutorial we will build the following pie chart using MindFusion Charts and Gauges for WinForms library:

This is a PieChart with a custom Series class that extends the capabilities of a standard PieSeries We need to create a custom Series class because we want the inner labels to follow a special format and we want the outer labels to be rendered as legend items not near pie pieces.

You can download the source code of the sample together with the MindFusion libraries used from the link at the bottom of the post.

I. General Setup

We create an empty WinForms application in C# with Visual Studio. We create a folder called References and there we copy the MindFusion.*.dll -s that we will need. They are:

MindFusion.Charting.dll
MindFusion.Charting.WinForms.dll
MindFusion.Common.dll

We reference those files in our project: we right-click the References folder in the Solution Explorer and click on “Add Reference”. Then we browse to our local References folder and add the 3 dll-s.

We have installed MindFusion Charts and Gauges from the website: http://mindfusion.eu/ChartWinFormsTrial.zip and now we need only to find the PieChart control, drag and drop it onto the WinForms Form of our application.

II. The Custom Pie Series

When you create a custom series you need to implement the Series interface. You need to declare several methods and properties, and one event. Let’s start with the constructor.

We need our chart to use one array with data and one array with labels. We declare two class variables for that and assign to them the values that we receive for them in the constructor. We name the new class CustomPieSeries:

public class CustomPieSeries : Series
{
	public CustomPieSeries(IList data, IList legendLabels )
	{
		values = data;		
		_legendLabels = legendLabels;

		//sum up all pie data
		total = 0.0;
		for (int i = 0; i < data.Count; i++)
			total += data[i];
	}

	IList values;	
	IList _legendLabels;
        double total = 0L;
}

We have added a new class variable called total. It is needed to keep the sum of all data for the chart. We will use this variable when we calculate the percent value of each pie piece.

The SupportedLabels property of type LabelKinds is the way to set which labels will be rendered by the new series. We want tooltips, inner labels and legend labels. There is no special enum field for legend labels. You just set which labels form the series should be rendered as legend items. We decide to use for this the ZAxisLabel, because it is not drawn anywhere on the pie chart and we won’t see it doubled by the legend labels. So, we say that the chart supports ZAxisLabels and we will tell the series that the ZAxisLabels must be rendered in the legend. We will do this later.

public LabelKinds SupportedLabels
{
	get { return LabelKinds.InnerLabel | LabelKinds.ToolTip | LabelKinds.ZAxisLabel; }
}

The GetLabel method is the place where we must return the correct label according to the type and position of the label, which are provided as arguments.

public string GetLabel(int index, LabelKinds kind)
{
	double percent = (values[index] / total) * 100;
	if (kind == LabelKinds.InnerLabel)
		return percent.ToString("F2") + "%\n" + values[index].ToString();
	if (kind == LabelKinds.ToolTip)
		return "Number of interns: " + values[index].ToString("F0") + 
			"\nPercent of total: " + percent.ToString("F2") + "%";

	if (kind == LabelKinds.ZAxisLabel)
		return _legendLabels[index].ToString();

	return null;
}

Here we have the chance to work over the raw data that we have for the series and return the desired labels as a string. We want the inner label to appear as the data value together with its percent representation. We calculate the percent thanks to the total variable and format it in an appropriate way:

 if (kind == LabelKinds.InnerLabel)
	return percent.ToString("F2") + "%\n" + values[index].ToString();

We do the same with the tooltips. We add an explanation text to the tooltip of each piece:

if (kind == LabelKinds.ToolTip)
	return "Number of interns: " + values[index].ToString("F0") + 
		"\nPercent of total: " + percent.ToString("F2") + "%";

The ZAxisLabel is the easiest to do. It will be used by the legend and we perform no special formatting upon it – we just return the label corresponding to the given index.

if (kind == LabelKinds.ZAxisLabel)
		return _legendLabels[index].ToString();

Among the other notable members of the Series interface are the Dimensions and Title properties. Dimensions is the property that specifies how many data dimensions the series has. They are 1 for charts that use one array of data, 2 for axes that use X and Y data, and 3 for 3D charts, which need X, Y and Z data. In our case we return 1 as property value because pie charts, just like radar charts, use only one data array.

public int Dimensions
{
	get { return 1; }
}

The Title property returns the Series title. This is an important property but in our case we will not use it. A common use case for Title is to be rendered in legends. We will not render the Series title in the legend, so we return an empty string.

public string Title
{
	get { return ""; }
}

Building the CustomPieSeries is an easy task:

var values = new List { 23, 54, 17, 9 };
pieChart1.Series = new CustomPieSeries(
	values,				
	new List()
	{
		" <1 month", " <=3 months", " <=6 months", " >6 months"
	}
);

We create a new instance of our new class and assign it to the Series property of the PieChart control. We provide a list with the data and the labels that we want to appear as a legend.

III. The Legend

The legend in the PieChart control is regulated by the LegendRenderer property. We set the ShowSeriesElements property to true to let the chart know that we want the data from the series to be rendered as labels and not the title:

pieChart1.LegendRenderer.ShowSeriesElements = true;

Then we use the ElementLabelKind property to specify which labels we want to use in the legend. These are the ZAxisLabels:

pieChart1.LegendRenderer.ElementLabelKind = LabelKinds.ZAxisLabel;

The other properties are self-explanatory. We use the Title property to set the legend title and set a transparent brush for both the Background and the BorderStroke = “Duration”;

pieChart1.LegendRenderer.Background = new SolidBrush(Color.Transparent);
pieChart1.LegendRenderer.BorderStroke = new SolidBrush(Color.Transparent);

IV. Styling

Styling the chart is done through the Theme property and through styles. There are different style classes available and in our case we will use the PerElementSeriesStyle class. This class accepts as arguments for the brushes and strokes lists with lists that contain the brushes. Then, it colors each element in each series with the corresponding brush in the array at the corresponding index. Our PieChart needs just one list with brushes and strokes. The stroke thicknesses are also added as nested arrays:

pieChart1.Plot.SeriesStyle = new PerElementSeriesStyle()
{
	Fills = new List<List>()
	{
		new List()
		{
			new SolidBrush(Color.FromArgb(158, 212, 224)),
			new SolidBrush(Color.FromArgb(187, 236, 247)),
			new SolidBrush(Color.FromArgb(212, 204, 196)),
			new SolidBrush(Color.FromArgb(245, 210, 179))					         }
	},

	Strokes = new List<list>()
	{
		new List()
		{
			new SolidBrush(Color.White)
						
		}
	},
	StrokeThicknesses = new List<list>()
	{
		new List()
		{
			3.0
		}
	}
};

We also set some appearance properties through the Theme field:

pieChart1.Theme.DataLabelsFontSize = 10;
pieChart1.Theme.HighlightStroke = new SolidBrush(Color.FromArgb(237, 175, 120));
pieChart1.Theme.HighlightStrokeThickness = 4;

The HighlightStroke is used to mark the selected chart element by mouse hover. The DataLabelsFontSize is used not only by painting the inner labels but also by painting the labels in the legend.

Finally we set the Title of the chart:

pieChart1.Title = "Internship Statistics";

And that is the end of this tutorial. You can download the code for the chart together with the MindFusion.*.dll libraries used from this link:

Pie Chart with Custom Labels Source Code Download

You can refer to MindFusion helpful support team with any technical questions regarding the WinForms Charts & Gauges control. Use the forum at: https://mindfusion.eu/Forum/YaBB.pl?board=mchart_disc

About MindFusion Charts and Gauges for WinForms: MindFusion Charts and Gauges for WinForms offers a long list of features that enables developer to build any type of chart, gauge or dashboard. The smart API of the library provides the option different chart elements like plots, axes, labels, and series to be combined and arranged in order to build any type of custom looking chart. The library also supports out of the box the most common chart types together with a set of their widely used properties. The gauge control is indispensable part of the library and offers a linear and oval gauge together with a variety of samples that provide you with the most common types of gauges used: clock, compass, thermometer, car dashboard and more. More about MindFusion Charts and Gauges for WinForms at: https://mindfusion.eu/winforms-chart.html.

Negative Bar Stack in WinForms

Thia post will demonstrate how to create a horizontal stacked bar chart with negative values. We will use the Charting for WinForms component and the final chart looks like this:

Negative Stacked Bar Chart in WinForms with the Chart Control

Negative Stacked Bar Chart in WinForms with the Chart Control

I. Data

The chart will use four series. The first two series are the ones that you clearly see – the red and blue one. We initialize two data arrays with random values that represent the data:

var firstBarX = new List(11);
var secondBarX = new List(11);

Random r = new Random();
            
for(int i = 0; i < 11; i++)
   {
       firstBarX.Add(2.2 + r.NextDouble());
       secondBarX.Add(2.4 + r.NextDouble());
    }

We need a list with data values that are exactly the same as the first series, but are negative. They will provide a transparent initial series, whose bars will offset the first, red, series as much as the value of the red bar should be:

//generate data for the transparent series
var transparentBarX = new List(firstBarX.Count);

for (var i = 0; i < firstBarX.Count; i++)			
    transparentBarX.Add(0 - firstBarX[i]);

The data for the Y-axis is very simple. We just need a list with the numbers from 1 to 12:

//data for the Y-axis
var barY = new List();
for (int i = 1; i < 12; i++)			
	barY.Add(i);

II. Labels
We will create two lists with the labels that we will show – for the two axis. The labels for the Y-axis will be used as tooltips as well. We just initialize two string lists:

//initialize labels for the X axis
var ageLabels = new List() { "0-5", "6-11", "12-17", "18-23", "24-29", "30-35",
		   "36-41", "42-47", "48-53", "54-59", "60-65" };

//initialize labels for the Y axis.
var percentageLabels = new List { "5%", "4%", "3%", "2%", "1%", "0%", 				"1%", "2%", "3%", "4%", "5%", };

We will use this label lists in the next section, when we create the series.

III. Series

We will use 4 series, of type Series2D. This series is useful for its SupportedLabels property – it allows us to specify what the labels of the series would be used for. We start with the transparent series. Its labels will be used for custom labels at the Y-axis:

var seriesTransparent = new MindFusion.Charting.Series2D(transparentBarX, barY, ageLabels);
seriesTransparent.SupportedLabels = MindFusion.Charting.LabelKinds.YAxisLabel;
seriesTransparent.Title = "";

We use for X-data the negative values you remember from the previous paragraph. The age labels are those that provide the data for the axis.

Then we initialize the two series for the chart data:

var seriesFirst = new MindFusion.Charting.Series2D(firstBarX, barY, ageLabels);
seriesFirst.SupportedLabels = MindFusion.Charting.LabelKinds.ToolTip;
seriesFirst.Title = "Female";

var seriesSecond = new MindFusion.Charting.Series2D(secondBarX, barY, ageLabels);
seriesSecond.SupportedLabels = MindFusion.Charting.LabelKinds.ToolTip;
seriesSecond.Title = "Male";

The last series that we create is just to provide data for the X-axis and we set its SupportedLabels property to MindFusion.Charting.LabelKinds.XAxisLabel. The X-data for this series corresponds to the coordinates of the X-labels, that’s why set the numbers from -5 to 5 as X-data:

var firstParamList = new List();
      for (int i = -5; i < 6; i++)			
	 firstParamList.Add(i);

The Y-data could be all zeros, we won’t use them anyway.

var secondParamList = new List();
    for (int i = 0; i < 12; i++)			
	secondParamList.Add(0);

And we create the series this way:

//the purpose of this series is to only supply the Xaxis labels, it is transparent.	
var seriesThird = new MindFusion.Charting.Series2D(firstParamList, secondParamList, percentageLabels);           	
seriesThird.SupportedLabels = MindFusion.Charting.LabelKinds.XAxisLabel;
seriesThird.Title = "";

After you have created all series, you should add them to the Series collection of the BarChart:

barChart.Series = new ObservableCollection
	{ seriesTransparent, seriesFirst, seriesSecond, seriesThird };
           	

IV. Axes and Grid

We need to fix the divisions of the X-axis if we want to show a grid and the grid to be neatly aligned with the axes. We will set the min value to be -6, the interval 1 and the max value to be 6:

barChart.XAxis.Interval = 1;
barChart.XAxis.MinValue = -6;
barChart.XAxis.MaxValue = 6;		

We will use three more properties to hide the interval labels at both axes, show ticks on them and set a title for each axis:

barChart.XAxis.Title = "Percentage of the Population";
barChart.YAxis.Title = "Ages";
			
barChart.ShowYCoordinates = false;
barChart.ShowXCoordinates = false;

barChart.ShowXTicks = true;
barChart.ShowYTicks = true;

Finally, we have to set the grid and specify that the bars are horizontal:

barChart.GridType = GridType.Vertical;
barChart.HorizontalBars = true;

V. Styling

Styling the chart is done though the SeriesSyle property of the Plot. You can assign to it different series types, you can check the list at the ISeriesStyle interface page in the documentation:

barChart.Plot.SeriesStyle = new PerSeriesStyle()
{
	Strokes = new List {
		firstBrush, new SolidBrush(Color.FromArgb(179, 0, 0)), 
                new SolidBrush(Color.FromArgb(0, 0, 102)), firstBrush
		},
		StrokeThicknesses = new List {
			 0,2,2,0
		},
		Fills = new List()
		{
			firstBrush, secondBrush, thirdBrush, firstBrush
	}
};

This style indicates that each brush from its collection will be used for one series in the chart. We assign to it 4 brushes and 4 strokes. The first brush is transparent, the second one is red, the other is blue and the last one is black – we won’t draw with it anyway.

The Theme property of the chart holds a lot of options for customizing the appearance. Here are just a few of them:

barChart.Theme.LegendBorderStrokeThickness = 1;
barChart.Theme.LegendBorderStroke = new SolidBrush(Color.Black);
barChart.Theme.GridColor2 = Color.White;
barChart.Theme.GridColor1 = Color.FromArgb(250, 250, 250);
barChart.Theme.GridLineColor = Color.FromArgb(153, 153, 153);
barChart.Theme.PlotBackground = new SolidBrush(Color.FloralWhite);
...........
..........

And that’s the end of this step-by-step guide. You can download the sample together will all necessary libraries from this link:

Download Negative Stack Bar Chart in CS Sample

About MindFusion Charting for WinForms: A versatile dashboard component that provides your WinForms application with the ability to create fascinating charts, interactive dashboards and practical gauges. The component combines a flexible API that allows custom combination of chart components to build any type of chart you want. You can add as many axes of any type you want, combine various chart series into a single chart with different data providers each. The control also supports pan and zoom, scroll, unlimited number of legends, grid and a dashboard panel. Linear and oval gauges complete the tool set and guarantee every feature you might need to build the perfect gauge, chart of any type or dashboard in WinForms is right at your fingertips. Learn more at https://mindfusion.eu/winforms-chart.html