Part 1: Dynamically Adding, Remove RenderableSeries with Code Behind

A common question that users ask us is how can they add and remove series dynamically on the chart? After all, SciChart is supposed to be High Performance Real-Time Charting is it not!

The following is a short tutorial on this topic and covers some of the pitfalls and potential problems which you might come across when developing dynamic applications with SciChart. We present a code-behind and MVVM approach to suit all architectures.

Code-Behind Method

In the following example we will use just code-behind to achieve adding/removing series dynamically. First, create a new WPF Application Project and paste the following code into the MainWindow:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="30" />
    <RowDefinition Height="*" />
  </Grid.RowDefinitions>

  <ToolBar>
    <Button Content="Add Series" Click="AddSeriesClick"></Button>
    <Button Content="Remove Series" Click="RemoveSeriesClick"></Button>
  </ToolBar>

  <SciChart:SciChartSurface SciChart:ThemeManager.Theme="ExpressionLight" Grid.Row="1" Name="SciChart">
      <SciChart:SciChartSurface.YAxes>
          <SciChart:NumericAxis TextFormatting="#.0" AxisTitle="YAxis"
                   AxisAlignment="Left" AutoRange="True"/>
      </SciChart:SciChartSurface.YAxes>
      <SciChart:SciChartSurface.XAxis>
        <SciChart:NumericAxis AxisTitle="X Axis"/>
      </SciChart:SciChartSurface.XAxis>
  </SciChart:SciChartSurface>
</Grid>

Next, add the following code to the code-behind:

public partial class AddRemoveSeries : Window
{
    private DataSeriesSet<double, double> _dataset;
    private Random _random;

    public AddRemoveSeries()
    {
        InitializeComponent();
        this.Loaded += AddRemoveSeries_Loaded;
        _random = new Random();
    }

    private void AddRemoveSeries_Loaded(object sender, RoutedEventArgs e)
    {
        _dataset = new DataSeriesSet<double, double>();
        SciChart.DataSet = _dataset;
    }

    private void AddSeriesClick(object sender, RoutedEventArgs e)
    {
    }

    private void RemoveSeriesClick(object sender, RoutedEventArgs e)
    {
    }
}

At this point if you run the application, you should get a blank chart with no data. Next, We need to add the series in the AddSeries click handler.

private void AddSeriesClick(object sender, RoutedEventArgs e)
{
	// Create a series
	var dataSeries = _dataset.AddSeries();

	// Add some random data with a random offset
	double offset = _random.NextDouble() * 5.0;
	for (int i = 0; i < 100; i++ )
	{
		dataSeries.Append(i, offset + _random.NextDouble());
	}                

	// Create a RenderableSeries
	var renderSeries = new FastLineRenderableSeries()
	{
		SeriesColor = Color.FromArgb(255,
		   (byte)_random.Next(0, 255),
		   (byte)_random.Next(0, 255),
		   (byte)_random.Next(0, 255))
	};

	SciChart.RenderableSeries.Add(renderSeries);
	SciChart.ZoomExtents();

	Console.WriteLine("DataSet Count: {0}, R-Series Index: {1}",
		_dataset.Count, renderSeries.DataSeriesIndex);
}

This will programmatically add a new DataSeries and RenderableSeries with random data (and a random color) to the chart. The result if you run the application and click Add Series a few times should be something like this:

Ok, next we will add the code to remove the series. In the RemoveSeries click handler, add the following:

private void RemoveSeriesClick(object sender, RoutedEventArgs e)
{
   if (_dataset.Count == 0) return;

   _dataset.RemoveAt(0);     

   var renderSeries = SciChart.RenderableSeries[0];
   SciChart.RenderableSeries.Remove(renderSeries);        

   SciChart.InvalidateElement();

   // Optional Logging
   Console.WriteLine("Removing RenderSeries with DataSeriesIndex={0}",
        renderSeries.DataSeriesIndex);
   Console.WriteLine("Remaining RenderSeries DataSeries Indices: {0}",
        string.Join(", ", SciChart.RenderableSeries.Select(
              x => x.DataSeriesIndex.ToString())));
}

Now if you run the application, click AddSeries 2x and RemoveSeries 1x you end up with a blank chart!

Oh dear. Now, if you added the optional logging you should see this in the output window of Visual Studio:

DataSeries Count: 1, RenderSeriesIndex: 0

DataSeries Count: 2, RenderSeriesIndex: 1

Removing RenderSeries with DataSeriesIndex=0

Remaining RenderSeries DataSeries Indices: 1

This gives us some clues as to what is happening here. If you haven’t read it, take a quick look at the tutorial Building a Simple Line Chart. In the section Creating the RenderableSeries there is a brief discussion on how RenderableSeries map to DataSeries. This relationship is many to one and internally, SciChart uses the RenderableSeries.DataSeriesIndex property to perform this mapping.

SciChartSurface.RenderableSeries map to DataSeries using the DataSeriesIndex

What is happening here is the RenderableSeries.DataSeriesIndex, which defaults to -1, is assigned an index equal to its index in the parent SciChartSurface.

e.g. Inside SciChart, this assignment is occurring

// Inside SciChartSurface, called whenever a RenderableSeries is added
private void AttachRenderableSeries(IRenderableSeries rSeries)
{
   // Unless the user assigned an index
   if (rSeries.DataSeriesIndex == -1) 
      return;

   // Assign a default index
   rSeries.DataSeriesIndex = this.RenderableSeries.IndexOf(rSeries);
}

So, when you add a number of RenderableSeries, the SciChartSurface assigns a default DataSeriesIndex if one has not already been assigned. This is all fine until you remove a RenderableSeries. The remaining RenderableSeries now have invalid DataSeriesIndex values.

To correct this, inside the RemoveSeriesClick handler, we add some code to reset the dataseries indexes of each RenderableSeries. The handler becomes:

private void RemoveSeriesClick(object sender, RoutedEventArgs e)
{
    if (_dataset.Count == 0)
        return;

    _dataset.RemoveAt(0);
    SciChart.RenderableSeries.RemoveAt(0);            

    // Re-order dataseries indices after remove
    for(int i = 0; i < SciChart.RenderableSeries.Count; i++)
    {
        SciChart.RenderableSeries[i].DataSeriesIndex = i;
    }

    SciChart.InvalidateElement();
}

Now, re-running the application you should see the following result when clicking Add Series, Add Series, Remove Series:

Downloads

Below you can download the Window.xaml and Window.xaml.cs that created this example:

Download: AddRemoveSeriesCodeBehind.zip

Stay tuned for Part 2: Add Remove Series with MVVM

2 comments

  1. Norbert Gooßens

Trackback e pingback

No trackback or pingback available for this article

Leave a Reply