Version

Chart Performance

Overview

This topic is your gateway to important conceptual and task-based information that will help you make the UltraDataChart™ control work as fast as possible in your application.

The topic is organized as follows:

Introduction

Although the UltraDataChart is capable of handling high volumes of data, there are several chart features that affect performance of the chart and they should be considered when optimizing performance in your application. Also, there are a few data patterns that should be considered while implementing the data source for the UltraDataChart control.

Chart Features

The following section lists chart features that add to the overhead and processing updates in the UltraDataChart control.

Series Types

The number of series defined in the UltraDataChart control’s Series collection contributes to the performance of the chart. Also, the type of series has some impact on the chart; for example, using LineSeries is recommended over usage of SplineSeries because of the complex interpolation of spline lines between data points. The same applies to PolarLineSeries and PolarSplineSeries or PolarSplineAreaSeries.

This code snippet shows how to replace SplineSeries with LineSeries in the UltraDataChart control.

In Visual Basic:

Dim splineSeries As New SplineSeries()
splineSeries.DataSource = data;
splineSeries.ItemsSource = data;
splineSeries.ValueMemberPath = "Value"
splineSeries.XAxis = categoryXAxis
splineSeries.YAxis = numericYAxis
Dim lineSeries As New LineSeries()
lineSeries.DataSource = data;
lineSeries.ItemsSource = data;
lineSeries.ValueMemberPath = "Value"
lineSeries.XAxis = Me.categoryXAxis
lineSeries.YAxis = Me.numericYAxis
Me.DataChart.Series.Add(splineSeries)Me.DataChart.Series.Add(lineSeries)

In C#:

var splineSeries = new SplineSeries();
splineSeries.DataSource = data;
splineSeries.ItemsSource = data;
splineSeries.ValueMemberPath = "Value";
splineSeries.XAxis = this.categoryXAxis;
splineSeries.YAxis = this.numericYAxis;
this.DataChart.Series.Add(splineSeries);var lineSeries = new LineSeries();
lineSeries.DataSource = data;
lineSeries.ItemsSource = data;
lineSeries.ValueMemberPath = "Value";
lineSeries.XAxis = this.categoryXAxis;
lineSeries.YAxis = this.numericYAxis;
this.DataChart.Series.Add(lineSeries);

Series Resolution

Setting the Resolution property on a Series to a higher value can help with performance, but it will lower the graphical fidelity of the line. As such, it can be increased up until the fidelity is unacceptable.

This code snippet shows how to increase resolution of a series in the UltraDataChart control.

In Visual Basic:

Dim series As New LineSeries()
series.Resolution = 1.5

In C#:

LineSeries series = new LineSeries();
series.Resolution = 1.5;

Markers

Markers are especially expensive when it comes to performance of the Data Chart because they add to the layout complexity of the chart, and perform data binding to obtain certain information. Their impact can be reduced somewhat by removing some of the bindings they perform from their templates. But, the additional layout complexity still adds to the overhead. Therefore, if markers are not needed, they should be removed from the chart by setting the MarkerType property of a Series to MarkerType enumerable value.

This code snippet shows how to remove markers from the UltraDataChart control.

In Visual Basic:

Dim series As New LineSeries()
series.MarkerType = MarkerType.None

In C#:

var series = new LineSeries();
series.MarkerType = MarkerType.None;

Axes Types

XAxis property. However, usage of CategoryDateTimeXAxis is not recommended if spaces between data points, based on the amount of time span between them, are not important. Instead, CategoryXAxis should be used because it is more efficient in the way it coalesces data, and it doesn’t perform any sorts on the data like the CategoryDateTimeXAxis does.

This code snippet shows how to replace CategoryDateTimeXAxis with CategoryXAxis in the UltraDataChart control.

In Visual Basic:

Dim categoryXAxis As New CategoryXAxis()
categoryXAxis.Label = "Date"
Dim categoryDateTimeXAxis As New CategoryDateTimeXAxis()
categoryDateTimeXAxis.DateTimeMemberPath = "Date"
categoryDateTimeXAxis.Label = "Date"

In C#:

var categoryXAxis = new CategoryXAxis();
categoryXAxis.Label = "Date";
var categoryDateTimeXAxis = new CategoryDateTimeXAxis();
categoryDateTimeXAxis.DateTimeMemberPath = "Date";
categoryDateTimeXAxis.Label = "Date";
Note
Note:

Turning off the axis gridlines does not currently improve the performance of the UltraDataChart.

Axis Labels

In the same way as Markers, axis labels are also expensive because they use templates and bindings, and may have their data context changed often. If labels are not used, they should be turned off on Axis.

This code snippet shows how to hide axis labels in the UltraDataChart control.

In Visual Basic:

Dim axis As New CategoryXAxis()
axis.LabelSettings = New AxisLabelSettings()
axis.LabelSettings.Visibility = Visibility.Collapsed
axis.LabelsVisible = false

In C#:

var axis = new CategoryXAxis();
axis.LabelSettings = new AxisLabelSettings();
axis.LabelSettings.Visibility = Visibility.Collapsed;
axis.LabelsVisible = false;

Axis Label Extent

The chart control adjust at runtime extent of labels on y-axis based on a label with longest value. This might decrease chart performance if range of data changes and labels need to be updated. Therefore it is recommended to set label extent at design time in order to improve chart performance. The following code snippet shows how to set a fixed extent for labels on y-axis in the UltraDataChart control.

In Visual Basic:

Dim axis As New NumericYAxis()axis.LabelSettings = New AxisLabelSettings()
axis.LabelSettings.Extent = 50
axis.LabelExtent = 50

In C#:

var axis = new NumericYAxis();
axis.LabelSettings = new AxisLabelSettings();
axis.LabelSettings.Extent = 50;
axis.LabelExtent = 50;

Data Implementation

Data Source

If changing only one or two points in data that is bound to the Series object’s DataSource property, you should avoid sending the Reset event from the INotifyCollectionChanged interface because the UltraDataChart control is optimized to better handle several Add, Remove, Replace, and Move events from the INotifyCollectionChanged interface. Sending one reset event instead of several smaller events can have a higher net cost than a lot of smaller operations with the DataSource performed in the same interaction.

This code snippet shows how to notify about changes in custom collection using the Add event action instead of the Reset event action when a new data point is added to the collection.

In Visual Basic:

Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.Specialized
Imports NotifyEventArgs = System.Collections.Specialized.NotifyCollectionChangedEventArgs
Imports NotifyAction = System.Collections.Specialized.NotifyCollectionChangedAction
Public Class DataCollection
    Implements INotifyCollectionChanged
    Implements IEnumerable
    Protected Data As New List(Of DataPoint)()
      Public Event CollectionChanged As NotifyCollectionChangedEventHandler Implements INotifyCollectionChanged.CollectionChanged
    Protected Sub OnCollectionChanged(e As NotifyEventArgs)
        RaiseEvent CollectionChanged(Me, e)
    End Sub
    Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return Me.Data.GetEnumerator()
    End Function
    Public Sub Add(dataPoint As DataPoint)
        Me.Data.Add(dataPoint)
        Dim e As New NotifyEventArgs(NotifyAction.Add, dataPoint)
        ' use the Add event action instead of the Reset event action
        ' when changing only one or two points
        'NotifyEventArgs e = new NotifyEventArgs(NotifyAction.Reset);
        Me.OnCollectionChanged(e)
    End Sub
End Class

In C#:

using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using NotifyEventArgs = System.Collections.Specialized.NotifyCollectionChangedEventArgs;
using NotifyAction = System.Collections.Specialized.NotifyCollectionChangedAction;
public class DataCollection : INotifyCollectionChanged, IEnumerable
{
        protected List<DataPoint> Data = new List<DataPoint>();
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        protected void OnCollectionChanged(NotifyEventArgs e)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, e);
            }
        }
        public IEnumerator GetEnumerator()
        {
            return this.Data.GetEnumerator();
        }
        public void Add(DataPoint dataPoint)
        {
            this.Data.Add(dataPoint);
            NotifyEventArgs e = new NotifyEventArgs(NotifyAction.Add, dataPoint);
            // use the Add event action instead of the Reset event action
            // when adding only one or two points to the collection
            //NotifyEventArgs e = new NotifyEventArgs(NotifyAction.Reset);
            this.OnCollectionChanged(e);
        }
}

Data Items

If the values of the data items in the bound collection won’t change their values, a faster alternative is to not implement the INotifyPropertyChanged interface.

If they do implement this interface, the data binding engine of the UltraDataChart control assumes it has to register handlers for each of them, and this increases overhead.

This code snippet shows a data item with and without implementation of the INotifyPropertyChanged interface.

In Visual Basic:

Imports System.ComponentModel
Public Class DataPoint
#Region "Properties"
    Public Property X() As Double
        Get
            Return _x
        End Get
        Set(ByVal value As Double)
            _x = Value
        End Set
    End Property
    Private _x As Double
    Public Property Y() As Double
        Get
            Return _y
        End Get
        Set(ByVal value As Double)
            _y = Value
        End Set
    End Property
    Private _y As Double
#End Region
End Class
Public Class ObservableDataPoint
    Implements INotifyPropertyChanged
#Region "Properties"
    Private _x As Double
    Public Property X() As Double
        Get
            Return _x
        End Get
        Set(ByVal value As Double)
            If _x = value Then Return
            _x = value
            OnPropertyChanged("X")
        End Set
    End Property
    Private _y As Double
    Public Property Y() As Double
        Get
            Return _y
        End Get
        Set(ByVal value As Double)
            If _y = value Then Return
            _y = value : OnPropertyChanged("Y")
        End Set
    End Property
#End Region
#Region "INotifyPropertyChanged"
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    Protected Sub OnPropertyChanged(ByVal propertyName As String)
        Me.OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
    End Sub
    Protected Sub OnPropertyChanged(ByVal propertyChangedEventArgs As PropertyChangedEventArgs)
        RaiseEvent PropertyChanged(Me, propertyChangedEventArgs)
    End Sub
#End Region
End Class

In C#:

using System.ComponentModel;
public class DataPoint
{
    #region Properties
    public double X { get; set; }
    public double Y { get; set; }
    #endregion
}
public class ObservableDataPoint : INotifyPropertyChanged
{
    #region Porperties
    private double _x;
    private double _y;
    public double X
    {
        get { return _x; }
        set { if (_x == value) return; _x = value; this.OnPropertyChanged("X"); }
    }
    public double Y
    {
        get { return _y; }
        set { if (_y == value) return; _y = value; this.OnPropertyChanged("Y"); }
    }
    #endregion
    #region Event Handlers
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    protected void OnPropertyChanged(PropertyChangedEventArgs propertyChangedEventArgs)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
            handler(this, propertyChangedEventArgs);
    }
    #endregion
}