)
We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. (Last updated on: June 24, 2019).
Unfortunately, activation email could not send to your email. Please try again.
Syncfusion Feedback

How to generate dynamic number of series based on common items source?

Platform: WPF |
Control: SfChart |
Published Date: March 21, 2017 |
Last Revised Date: June 27, 2019

Description:

This article describes how to generate dynamic number of series by binding a collection (Items source) directly to chart.

Solution:

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.

The implementation process is explained in the following steps.

Step 1: Define chart’s view model setup

This step describes how to define the chart’s collection (items source) through MVVM.

Model

C#

 
public class PriceData
{   
    public string Component { get; set; }
 
    public double Price { get; set; }
}
 

C#

 
public class AnnualPriceData
{
    public string Year { get; set; }
 
    public ObservableCollection<PriceData> PriceCollection { get; set; }
}
 

View Model

C#

public class ViewModel
    {
        public ObservableCollection<AnnualPriceData> AnnualPriceCollection { get; set; }
 
        public ViewModel()
        {
            AnnualPriceCollection = new ObservableCollection<AnnualPriceData>();
 
            AnnualPriceCollection.Add(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  },
                }
            });
            
            AnnualPriceCollection.Add(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  },
                }
            });
 
            AnnualPriceCollection.Add(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  },
                }
            });
 
            AnnualPriceCollection.Add(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  },
                }
            });
 
            AnnualPriceCollection.Add(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 SfChart extension

To achieve this requirement, use the custom SfChart, which inherits from SfChart and defines two properties, namely the Source and SeriesTemplate properties.

Source:

The Source property is used to bind the items source (collection of collections) to chart. The chart series will be generated per item in the collection.

C#

 
//Gets or sets the ItemsSource (collection of collections).
 
public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", typeof(object), typeof(SfChartExt), new                                             
        PropertyMetadata(null, OnSourceChanged));
 
public object Source
{
    get { return (object)GetValue(SourceProperty); }
    set { SetValue(SourceProperty, value); }
}
 

SeriesTemplate:

The SeriesTemplate property is used to define the type and visual appearance of chart series. This template is more flexible, and it allows you to define any type of series and all its properties since the content of the template is the series.

C#

 
//Gets or sets the template for the series to be generated.
 
public static readonly DependencyProperty SeriesTemplateProperty =
        DependencyProperty.Register("SeriesTemplate", typeof(DataTemplate),    
        typeof(SfChartExt), new PropertyMetadata(null, OnSeriesTemplateChanged));
 
public DataTemplate SeriesTemplate
{
    get { return (DataTemplate)GetValue(SeriesTemplateProperty); }
    set { SetValue(SeriesTemplateProperty, value); }
}
 

The following code illustrates generating dynamic number of series based on data(collection).

C#

 
public class SfChartExt :  SfChart
{
 
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(object), typeof(SfChartExt), new PropertyMetadata(null, OnPropertyChanged));
        
public static readonly DependencyProperty SeriesTemplateProperty =
DependencyProperty.Register("SeriesTemplate", typeof(DataTemplate), typeof(SfChartExt), new PropertyMetadata(null, OnPropertyChanged));
        
//Gets or sets the ItemsSource of collection of collections.
 
public object Source
{
  get { return (object)GetValue(SourceProperty); }
  set { SetValue(SourceProperty, value); }
}
 
//Gets or sets the template for the series to be generated.
 
public DataTemplate SeriesTemplate
{
   get { return (DataTemplate)GetValue(SeriesTemplateProperty); }
   set { SetValue(SeriesTemplateProperty, value); }
}
 
private static void OnPropertyChanged(DependencyObject d,                              DependencyPropertyChangedEventArgs e)     
{
  (d as SfChartExt).GenerateSeries();
}
 
//Generate the series per the counts in the itemssource.
private void GenerateSeries()
{
    if (Source == null || SeriesTemplate == null) 
        return;                
            
    var commonItemsSource = (Source as IEnumerable).GetEnumerator();
 
    while(commonItemsSource.MoveNext())
    {
         ChartSeries series = SeriesTemplate.LoadContent() as ChartSeries;
         series.DataContext = commonItemsSource.Current;
         Series.Add(series);
    }   
 }
}
 

XAML

<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 illustrates the result of above code examples.

Output

Collection of chart series generated by single item source in WPF

To generate different types of chart series

You can generate multiple types of series by using the DataTemplateSelector property. A new class of SeriesDataTemplateSelector can be created by inheriting the DataTemplateSelector to achieve this requirement.

C#

public class SeriesDataTemplateSelector : DataTemplateSelector
{
   public DataTemplate ColumnSeriesTemplate { get; set; }
   public DataTemplate LineSeriesTemplate { get; set; }
   public override DataTemplate SelectTemplate(object item, DependencyObject 
                                                                              container)
   {
       //Check required conditions and return the selected template.
       
       DataTemplate selectedDataTemplate = null;
 
       string year = (item as AnnualPriceData).Year;
 
       if (year == "2015" || year =="2016")
           selectedDataTemplate = LineSeriesTemplate;
       else
           selectedDataTemplate = ColumnSeriesTemplate;
 
           return selectedDataTemplate;    
   }       
}
 

SfChart extension with SeriesTemplateSelector:

The SeriesTemplateSelector property is used to select the different series template in single chart based on item.

C#

 
public class SfChartExt : SfChart
{
 
public static readonly DependencyProperty SeriesTemplateSelectorProperty =
DependencyProperty.Register("SeriesTemplateSelector", typeof(SeriesDataTemplateSelector), typeof(SfChartExt), new PropertyMetadata(null, OnPropertyChanged));
 
//Get or sets the DataTemplateSelector for the multiple series generation.
public SeriesDataTemplateSelector SeriesTemplateSelector
{
   get { return (SeriesDataTemplateSelector)GetValue(SeriesTemplateSelectorProperty); }
   set { SetValue(SeriesTemplateSelectorProperty, value); }
}  
 
//Generate the series according to the counts in the itemssource.
private void GenerateSeries()
{
    if (Source == null || (SeriesTemplateSelector == null && SeriesTemplate == null)) 
        return;     
     var commonItemsSource = (Source as IEnumerable).GetEnumerator();
 
     while(commonItemsSource.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(commonItemsSource.Current, 
                                                                                   null);     
              series = selectedseriesTemplate.LoadContent() as ChartSeries;
          }
 
          series.DataContext = commonItemsSource.Current;
          Series.Add(series);
                
            }
        }
}
 

SeriesDataTemplateSelector definition:

The different types of templates are defined in the resources for the SeriesTemplateSelector property.

XAML

<Grid.Resources>
 
    <DataTemplate x:Key="columnSeriesTemplate">
        <chart:ColumnSeries XBindingPath="Component" YBindingPath="Price" 
                                          ItemsSource="{Binding PriceCollection}"
                                          Label="{Binding Year}"/>
    </DataTemplate>
            
    <DataTemplate x:Key="lineSeriesTemplate" >
        <chart:LineSeries XBindingPath="Component" YBindingPath="Price" 
                                    ItemsSource="{Binding PriceCollection}"
                                    Label="{Binding Year}"/>
    </DataTemplate>
 
</Grid.Resources> 
 
<local:SfChartExt Source="{Binding AnnualPriceCollection}">
 
<!--The SeriesDataTemplateSelector is defined for generating the series.-->
            <local:SfChartExt.SeriesTemplateSelector>
                <local:SeriesDataTemplateSelector 
                                                  ColumnSeriesTemplate="{StaticResource
                                                  columnSeriesTemplate}"                    
                                                  LineSeriesTemplate="{StaticResource 
                                                  lineSeriesTemplate}" />
 
            </local:SfChartExt.SeriesTemplateSelector> 
 
</local:SfChartExt>
 
 

The following column and line series (multiple series) are created as the result of above code examples.

Output

Collection of multiple types of chart series generated by single item source in WPF

Please find the required sample from the link.

2X faster development

The ultimate WPF UI toolkit to boost your development speed.
ADD COMMENT
You must log in to leave a comment
Comments
Sumanth R
Apr 29, 2019

Hi,

Can you please give insights on how to perform the same above example of Dynamic Series generation in Xamarin Forms?

Reply
Hemalatha Marikumar [Syncfusion]
Nov 05, 2019

Hi Sumanth R,

We have prepared a sample based on your requirement in Xamarin.Forms and you can download the sample from the below link.

Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/SimpleSample1096156904.zip

Regards, Hemalatha M.

Michael Steiner
Jun 04, 2020

Hi,

This is a very helpful example. What I would like to do is add the data points dynamically from a background thread (e.g. on a timer elapsed event). What I found is that this does not work even when using thread safe collections. The only way I could make it work is creating a CollectionViewSource which binds to a thread safe collection in my ViewModel and then bind the ItemsSource of the Series to this CollectionViewSource. What I can't figure out is how to implement this in this dynamic approach. Any help would be greatly appreciated.

Reply
Suyamburaja Jayakumar [Syncfusion]
Jun 17, 2020

Hi Michael Steiner,

We have prepared a sample based on your requirement in dynamically add the datapoints from a background thread (Dispatcher timer) and you can download the sample from the below link.

Sample link: https://www.syncfusion.com/downloads/support/directtrac/general/ze/COMMON~1-1145185723.zip

Regards, Suyamburaja J.

Please sign in to access our KB

This page will automatically be redirected to the sign-in page in 10 seconds.

Up arrow icon

Warning Icon You are using an outdated version of Internet Explorer that may not display all features of this and other websites. Upgrade to Internet Explorer 8 or newer for a better experience.Close Icon

Live Chat Icon For mobile
Live Chat Icon