Charts integrated within ComponentOne FlexPivot for WinForms are now rendered using FlexChart. FlexChart has a modern look and is more powerful than the legacy C1Chart control used in older versions.
In this post, we'll walk through the new chart features of FlexPivot and show a real-world sample analyzing data.
To support backward compatibility, we kept the old charts within the C1FlexPivot component. In this last release we introduced a new FlexPivot control that uses the new FlexChart.
FlexPivot consists of three major components: PivotPanel, PivotChart, and PivotGrid. C1FlexPivot and FlexPivot differ by the component they use in PivotChart charting functionalities. C1FlexPivot used C1FlexPivotChart which was based on C1Chart control.
FlexChart has been a ComponentOne .NET flagship charting component (since 2016). Here are several advantages of FlexChart:
Let’s see how FlexPivot uses FlexChart. Using FlexChart as its underlying charting component, FlexPivot offers the following features:
Let’s consider a scenario where a research and data publication agency needs to show global COVID-19 statistics. In this example, the agency needs a tool that allows dynamic data analysis and data visualization.
Additionally, as the COVID-19 data is presented for each date of a specific time frame, the data points will be large. The control should allow run-time interaction enabling the user to analyze the data at a granular level, by selecting the area they want to enlarge.
FlexPivot and FlexChart can be used for this example. The below image represents global COVID-19 statistics:
Here, each data point in the chart represents one of the following COVID-19 data points:
We’re dividing the implementation into the following parts:
Binding FlexPivot is simple. Just set the property to DataSource. To show how easy it is to use FlexPivot to visualize real-time data, we'll display data for ‘COVID-19 information’ from OurWorldInData. The data taken for consideration contains information for the time span: December 31st, 2019 - May 31st, 2020.
To reflect this data in FlexPivot, we need to have a blueprint of the data to which we can bind our pivot. We will create the following class for this:
public class CovidData
{
public DateTime Date { get; set; }
public double TotalCases { get; set; }
public double TotalDeaths { get; set; }
public double NewCases { get; set; }
public double NewDeaths { get; set; }
}
Once we have created the blueprint of our data, we can now bind FlexPivot and add fields to RowValues and ValueFields:
var data = DataLoader.Import(countryName);
flexPivotPage.DataSource = data;
flexPivotPage.FlexPivotEngine.RowFields.Add("Date");
flexPivotPage.FlexPivotEngine.ValueFields.Add("TotalCases");
flexPivotPage.FlexPivotEngine.ValueFields.Add("NewCases");
flexPivotPage.FlexPivotEngine.ValueFields.Add("TotalDeaths");
flexPivotPage.FlexPivotEngine.ValueFields.Add("NewDeaths");
The following image shows data loaded in the FlexPivot after completion of this step:
Here, the chart renders the COVID-19 statistics: total cases, total deaths, new cases, and new deaths for each date.
Notice that the data set is large and the changes in data points appear very small. Therefore, in the next sections, we’ll cover how to customize the chart to create effective visualizations.
To make the data more meaningful to the user, we need to access the underlying FlexChart charting component in FlexPivot, then customize it.
In FlexPivot, FlexChart can be accessed through the FlexPivotChart.Chart property as follows:
var flexChart = flexPivotPage.FlexPivotChart.Chart as FlexChart;
Once we have access to FlexChart we can take advantage of its rich features and customize the chart output in FlexPivot.
The following section illustrates how to implement FlexChart features:
To improve data presentation, we will render COVID-19 parameters with different chart types.
With C1FlexPivot, we could display only two chart types as C1Chart had only two ChartGroups available. Whereas, with the FlexPivot, we can specify the chart type for an unlimited number of series.
To access the series and change their ChartType, we use the CollectionChanged event of the Series collection, as follows:
// gets FlexChart's Series collection
var seriesColl = flexChart.Series as ObservableCollection<Series>;
seriesColl.CollectionChanged += SeriesColl_CollectionChanged;
private void SeriesColl_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// handles only if a Series is added to FlexChart
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
var series = e.NewItems[e.NewItems.Count - 1] as Series;
if (series == null)
return;
switch (series.Name)
{
case "TotalCases":
series.ChartType = C1.Chart.ChartType.Line;
break;
case "NewCases":
series.ChartType = C1.Chart.ChartType.Step;
break;
case "TotalDeaths":
series.ChartType = C1.Chart.ChartType.Column;
break;
case "NewDeaths":
series.ChartType = C1.Chart.ChartType.Column;
break;
}
}
}
The value of COVID-19 parameters varies. For instance, the new COVID-19 cases on May 18th are 81,217, whereas total cases are 4,679,764. If we try to visualize multiple series in one plot-area, the series ‘NewCases’ with smaller value will become insignificant w.r.t ‘TotalCases.’ Therefore, we need to add an axis for each series.
With C1Chart, we could plot a maximum of three axes; however, FlexChart allows us to plot one or more data series on a secondary axis. To add axis for each series, we again use the CollectionChanged event and set the AxisY property in a way that the above code becomes as follows:
private void SeriesColl_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// handles only if a Series is added to FlexChart
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
var series = e.NewItems[e.NewItems.Count - 1] as Series;
if (series == null)
return;
switch (series.Name)
{
case "TotalCases":
series.ChartType = C1.Chart.ChartType.Line;
series.AxisY = new Axis()
{
AxisLine = true,
Position = C1.Chart.Position.Left,
Title = "Total patients diagnosed +ve with COVID-19",
Format = "0,0"
};
break;
case "NewCases":
series.ChartType = C1.Chart.ChartType.Step;
series.AxisY = new Axis()
{
Position = C1.Chart.Position.Left,
AxisLine = true,
Title = "Daily new patients diagnosed +ve with COVID-19"
};
break;
case "TotalDeaths":
series.ChartType = C1.Chart.ChartType.Column;
series.AxisY = new Axis()
{
AxisLine = true,
Position = C1.Chart.Position.Right,
Title = "Total deaths due to COVID-19",
Format = "0,0"
};
break;
case "NewDeaths":
series.ChartType = C1.Chart.ChartType.Column;
series.AxisY = new Axis()
{
Position = C1.Chart.Position.Right,
AxisLine = true,
Title = "New deaths due to COVID-19"
};
break;
}
}
}
The data set used in this article contains COVID-19 information for each date from December 31st, 2019 - May 31st, 2020. As the data becomes larger, the elements rendered against each data point become smaller, which becomes difficult to understand.
FlexChart resolves this problem by exposing a scrollbar that allows decreasing the range of data points in view. FlexPivot extends this feature by exposing a property FlexPivotChart.UseAxisScrollbar that automatically shows the Axis scrollbar when the number of labels/data points to render is large. Just use the scrollbar to increase/decrease the data in the view.
flexPivotPage.FlexPivotChart.UseAxisScrollbar = true;
The data under observation can range from having a few tens of data points to a few thousand. With so many points, it would be ideal to have zooming capabilities.
To enable zoom in FlexChart, we need to handle MouseDown, MouseUp, and MouseMove events.
Point _start, _last;
bool _IsMouseDown;
private void FlexChart_MouseUp(object sender, MouseEventArgs e)
{
var flexChart = sender as FlexChart;
// resets zooming in FlexChart
if(e.Button== MouseButtons.Right)
{
flexChart.AxisX.Min = flexChart.AxisX.Max = double.NaN;
flexChart.AxisY.Min = flexChart.AxisY.Max = double.NaN;
}
// stops Zooming
_IsMouseDown = false;
if (!_last.IsEmpty)
{
var start = flexChart.PointToData(_start);
var last = flexChart.PointToData(_last);
// updates axes with new limits
flexChart.AxisX.Min = Math.Min(start.X, last.X);
flexChart.AxisX.Max = Math.Max(start.X, last.X);
flexChart.AxisY.Min = Math.Min(start.Y, last.Y);
flexChart.AxisY.Max = Math.Max(start.Y, last.Y);
}
// clean up
_start = _last = Point.Empty;
}
private void FlexChart_MouseDown(object sender, MouseEventArgs e)
{
var flexChart = sender as FlexChart;
// starts Zooming
_IsMouseDown = true;
_start = e.Location;
_last = Point.Empty;
}
private void Chart_MouseMove(object sender, MouseEventArgs e)
{
var flexChart = sender as FlexChart;
// when zooming, update selection range
if (_IsMouseDown)
{
var ptCurrent = e.Location;
var left = (int)flexChart.PlotRect.Left;
var right = (int)flexChart.PlotRect.Right;
var top = (int)flexChart.PlotRect.Top;
var bot = (int)flexChart.PlotRect.Bottom;
ptCurrent.X = ptCurrent.X < left ? left : ptCurrent.X > right ? right : ptCurrent.X;
ptCurrent.Y = ptCurrent.Y < top ? top : ptCurrent.Y > bot ? bot : ptCurrent.Y;
_last = ptCurrent;
flexChart.Refresh();
}
}
With FlexChart, we can also focus on specific COVID-19 parameters by enabling only a specific series and disabling the rest. This can be done by setting the LegendToggle property to true.
flexChart.LegendToggle = true;
Here, the series ‘TotalDeaths’ and ‘TotalCases’ have been disabled so that their data is no more visible in the chart.
FlexChart has built-in support for selection. By enabling the Selection Mode, users can differentiate the selected COVID-19 parameter from other parameters and display information related to the selected parameter.
For example, if we want to display information of all COVID-19 parameters as a tooltip when the user selects a data point, we can access information for the selected data point.
For this, we will handle the MouseDown and MouseMove events of FlexChart as follows:
flexChart.SelectionMode = ChartSelectionMode.Point;
private void FlexChart_MouseDown(object sender, MouseEventArgs e)
{
var flexChart = sender as FlexChart;
var hitTestInfo = flexChart.HitTest(e.Location);
if (hitTestInfo == null || hitTestInfo.Item == null)
return;
if (hitTestInfo.ChartElement == ChartElement.PlotArea)
{
_selectedLocation = e.Location;
}
}
private void Chart_MouseMove(object sender, MouseEventArgs e)
{
var flexChart = sender as FlexChart;
// get details for selected element
CovidData selectedData = (((flexChart.Parent as FlexPivotChart).DataSource as C1FlexPivotPanel).DataSource as List<CovidData>).OrderBy(c => c.TotalCases).ToList()[flexChart.SelectedIndex] as CovidData;
if (selectedData != null)
{
var tooltipTemplate = @"**Date: {0}" + Environment.NewLine + "**Total Cases: {1}" + Environment.NewLine + "**Total Deaths: {2}" + Environment.NewLine + "**New Cases: {3}" + Environment.NewLine + "**New Deaths: {4}";
var tooltipContent = String.Format(tooltipTemplate, selectedData.Date.ToLongDateString(), selectedData.TotalCases, selectedData.TotalDeaths, selectedData.NewCases, selectedData.NewDeaths);
flexChart.ToolTip.Content = tooltipContent;
}
}
Here, once a data point is selected, information for all COVID-19 parameters is shown as its ToolTip.
FlexPivot introduces multiple easy-to-use chart palettes. Selected this by setting the Palette property.
flexPivotPage.Palette = Palette.Darkly;
The above image shows FlexPivot chart ‘FlexChart’ with the Palette ‘Darkly.’ With the above chart, it's easier to plot and understand the COVID-19 information with multiple secondary axes, different chart types, customized tooltip information, and the ability to scroll and zoom to any specific date range.
We have plans to extend FlexChart support in FlexPivot by adding more chart types and functionalities.
The inclusion of FlexChart will add many benefits to the FlexPivot control. Users have the ability to use multiple secondary axes, zooming in/out, scrollbar functionality with large data sets, composite charts, the ability to view the data by legend toggling selectively, and enhanced tooltips.
Our development team plans to add support for additional FlexChart features in upcoming releases!