How to generate dynamic Chart Series in UWP SfChart?
This article describes how to generate dynamic number of chart series by binding a ItemsSource collection directly to an SfChart.
The solution for achieving this scenario is quite easy. It can be achieved by extending the SfChart (inheriting SfChart), and providing it with the SeriesTemplate (for customizing series type and its properties) and Source properties.
Step 1: Define the chart’s ViewModel setup
This step describes how to define the chart’s ItemsSource collection through MVVM.
Models
public class PriceData
{
public string Component { get; set; }
public double Price { get; set; }
}
public class AnnualPriceData
{
public string Year { get; set; }
public ObservableCollection<PriceData> PriceCollection { get; set; }
}ViewModel
public class ViewModel
{
public ObservableCollection<AnnualPriceData> AnnualPriceCollection { get; set; }
public ViewModel()
{
AnnualPriceCollection = new ObservableCollection<AnnualPriceData>
{
new AnnualPriceData
{
Year = "2012",
PriceCollection = new ObservableCollection<PriceData>
{
new PriceData { Component = "Hard Disk", Price = 80 },
new PriceData { Component = "Scanner", Price = 140 },
new PriceData { Component = "Monitor", Price = 150 },
new PriceData { Component = "Printer", Price = 180 },
}
},
new AnnualPriceData
{
Year = "2013",
PriceCollection = new ObservableCollection<PriceData>
{
new PriceData { Component = "Hard Disk", Price = 87 },
new PriceData { Component = "Scanner", Price = 157 },
new PriceData { Component = "Monitor", Price = 155 },
new PriceData { Component = "Printer", Price = 192 },
}
},
new AnnualPriceData
{
Year = "2014",
PriceCollection = new ObservableCollection<PriceData>
{
new PriceData { Component = "Hard Disk", Price = 95 },
new PriceData { Component = "Scanner", Price = 150 },
new PriceData { Component = "Monitor", Price = 163 },
new PriceData { Component = "Printer", Price = 185 },
}
},
new AnnualPriceData
{
Year = "2015",
PriceCollection = new ObservableCollection<PriceData>
{
new PriceData { Component = "Hard Disk", Price = 113 },
new PriceData { Component = "Scanner", Price = 165 },
new PriceData { Component = "Monitor", Price = 175 },
new PriceData { Component = "Printer", Price = 212 },
}
},
new AnnualPriceData
{
Year = "2016",
PriceCollection = new ObservableCollection<PriceData>
{
new PriceData { Component = "Hard Disk", Price = 123 },
new PriceData { Component = "Scanner", Price = 169 },
new PriceData { Component = "Monitor", Price = 184 },
new PriceData { Component = "Printer", Price = 224 },
}
},
};
}
}Step 2: Implement the SfChart extension
Create a custom SfChartExt class which inherits from the base SfChart. This custom class defines two key properties: Source and SeriesTemplate.
public class SfChartExt : SfChart
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source),
typeof(object), typeof(SfChartExt),
new PropertyMetadata(null, OnSourceChanged));
public static readonly DependencyProperty SeriesTemplateProperty = DependencyProperty.Register(nameof(SeriesTemplate),
typeof(DataTemplate), typeof(SfChartExt),
new PropertyMetadata(null, OnSeriesTemplateChanged));
public static readonly DependencyProperty SeriesTemplateSelectorProperty = DependencyProperty.Register(nameof(SeriesTemplateSelector),
typeof(SeriesDataTemplateSelector),
typeof(SfChartExt),
new PropertyMetadata(null, OnSeriesTemplateChanged));
/// <summary>
/// Gets or sets a collection of collections, where each inner collection represents the
/// data for a single chart series.
/// </summary>
public object Source
{
get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
/// <summary>
/// Gets or sets the DataTemplate used to generate each chart series.
/// </summary>
public DataTemplate SeriesTemplate
{
get { return (DataTemplate)GetValue(SeriesTemplateProperty); }
set { SetValue(SeriesTemplateProperty, value); }
}
/// <summary>
/// Gets or sets a SeriesTemplateSelector which will be used to choose a DataTemplate for each group in the source.
/// </summary>
public SeriesDataTemplateSelector SeriesTemplateSelector
{
get { return (SeriesDataTemplateSelector)GetValue(SeriesTemplateSelectorProperty); }
set { SetValue(SeriesTemplateSelectorProperty, value); }
}
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as SfChartExt).GenerateSeries();
}
private static void OnSeriesTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as SfChartExt).GenerateSeries();
}
/// <summary>
/// Generates chart series based on the current data source and configured series templates.
/// </summary>
/// <remarks>
/// This method creates and adds a chart series for each item in the data source,
/// using either the specified <see cref="SeriesTemplate"/> or the <see cref="SeriesTemplateSelector"/>.
/// Each generated series is bound to its corresponding group in the source.
/// </remarks>
private void GenerateSeries()
{
if (Source == null || (SeriesTemplateSelector == null && SeriesTemplate == null)) return;
var groupedItemsSource = Source as IEnumerable;
var enumerator = groupedItemsSource.GetEnumerator();
while (enumerator.MoveNext())
{
ChartSeries series = null;
// The conditions checked for setting the SeriesTemplate or SeriesTemplateSelector.
if (SeriesTemplate != null)
{
series = SeriesTemplate.LoadContent() as ChartSeries;
}
else if (SeriesTemplateSelector != null)
{
var selectedseriesTemplate = SeriesTemplateSelector.SelectTemplate(enumerator.Current);
series = selectedseriesTemplate.LoadContent() as ChartSeries;
}
series.DataContext = enumerator.Current;
Series.Add(series);
}
}
}The SeriesTemplate property is used to define the type and visual appearance of chart series. This template is more flexible; it allows us to define any type of series and all its properties since the content of the template is series.
<local:SfChartExt Source="{Binding AnnualPriceCollection}" >
<--The SeriesTemplate is defined for generating the series.-->
<local:SfChartExt.SeriesTemplate>
<DataTemplate>
<chart:ColumnSeries XBindingPath="Component"
YBindingPath="Price"
ItemsSource="{Binding PriceCollection}"
Label="{Binding Year}"/>
</DataTemplate>
</local:SfChartExt.SeriesTemplate>
<--Define required chart properties.-->
</local:SfChartExt>The following column chart illustrates the result of above code.
Output
To generate different types of chart series
We can generate multiple types of series by using the DataTemplateSelector property. A new class of SeriesDataTemplateSelector can be created by inheriting the DataTemplateSelector property.
public class SeriesDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ColumnSeriesTemplate { get; set; }
public DataTemplate LineSeriesTemplate { get; set; }
public new DataTemplate SelectTemplate(object item)
{
DataTemplate selectedDataTemplate;
string year = (item as AnnualPriceData).Year;
if (year == "2015" || year == "2016")
{
selectedDataTemplate = LineSeriesTemplate;
}
else
{
selectedDataTemplate = ColumnSeriesTemplate;
}
return selectedDataTemplate;
}
}SeriesDataTemplateSelector definition
The different types of templates are defined in the resources for the SeriesTemplateSelector property.
<!--Resources for the SeriesTemplateSelector case.-->
<Grid.Resources>
<DataTemplate x:Key="ColumnSeriesDataTemplate">
<chart:ColumnSeries ItemsSource="{Binding PriceCollection}"
XBindingPath="Component" YBindingPath="Price"
Label="{Binding Year}" />
</DataTemplate>
<DataTemplate x:Key="LineSeriesDataTemplate" >
<chart:LineSeries ItemsSource="{Binding PriceCollection}"
XBindingPath="Component" YBindingPath="Price"
Label="{Binding Year}" />
</DataTemplate>
</Grid.Resources>
<local:SfChartExt>
. . .
<!--The SeriesDataTemplateSelector is defined for generating the series.-->
<local:SfChartExt.SeriesTemplateSelector>
<local:SeriesDataTemplateSelector ColumnSeriesTemplate="{StaticResource ColumnSeriesDataTemplate}"
LineSeriesTemplate="{StaticResource LineSeriesDataTemplate}" />
</local:SfChartExt.SeriesTemplateSelector>
. . .
</local:SfChartExt>Output
The following column and line series (multiple series) illustrates the result of above code.
Refer to the Github sample to examine the full working implementation.
Conclusion
I hope you enjoyed learning about generating a collection of series based on a single ItemsSource in an UWP Chart.
You can refer to our UWP Chart’s feature tour page to know about its other groundbreaking feature representations. You can also explore our UWP Chart documentation to understand how to present and manipulate data.
For current customers, you can check out our WinForms components from the License and Downloads page. If you are new to Syncfusion®, you can try our 30-day free trial to check out our UWP Charts and other UWP components.
If you have any queries or require clarifications, please let us know in comments below. You can also contact us through our support forums, Direct-Trac, or feedback portal. We are always happy to assist you!