This topic demonstrates, with code examples, how to animate your charts using the Motion Framework™ in the XamDataChart™ control. Procedures for both general and custom implementations are provided.
The topic is organized as follows:
The Motion Framework allows you to animate changes in data over time in the XamDataChart control, creating an immersive and animated user experience that "tells the story" of your data.
Figure 1: The XamDataChart control with the Motion Framework animating six data sources over time
The Motion Framework is managed by the TransitionDuration and the TransitionEasingFunction properties of Series object. These properties are available on all types of series in the Data Chart.
Table 1: Properties of the Motion Framework
The Motion Framework has the following limitation in the XamDataChart control: if the range of an axis changes as a result of a data change, no animation will begin.
The general implementation of the Motion Framework gives you full control over how the XamDataChart will animate data bound to it. However this implementation requires configuring each series in the XamDataChart control individually and providing a method for updating data. To start using the Motion Framework in the XamDataChart:
Set the TransitionDuration property. The TransitionDuration property controls the time for shifting the points of the series from their previous values to their next values. On the series you want to animate, set that property to a timespan that would provide smooth series’s transition. A of 0.5 seconds would be a good choice in most cases.
Set the TransitionEasingFunction property to the desired easing function or linear easing function that will be used to morph the series.
The code snippet below shows how to set TransitionDuration TransitionEasingFunction properties on ScatterLineSeries. The same logic can be applied to any other type of series supported in the XamDataChart.
In XAML:
<ig:XamDataChart x:Name="DataChart" > <ig:XamDataChart.Series> <ig:ScatterLineSeries TransitionDuration="0:0:0.5"> <ig:ScatterLineSeries.TransitionEasingFunction> <CubicEase EasingMode="EaseOut" /> </ig:ScatterLineSeries.TransitionEasingFunction> </ig:ScatterLineSeries > </ig:XamDataChart.Series> </ig:XamDataChart>
In Visual Basic:
Imports System.Windows.Media.Animation Imports Infragistics.Controls.Charts ' ... Dim series As New ScatterLineSeries() series.TransitionDuration = TimeSpan.FromSeconds(0.5) series.TransitionEasingFunction = New CubicEase() With { .EasingMode = EasingMode.EaseOut } Me.DataChart.Series.Add(series)
In C#:
using System.Windows.Media.Animation; using Infragistics.Controls.Charts; // ... ScatterLineSeries series = new ScatterLineSeries(); series.TransitionDuration = TimeSpan.FromSeconds(0.5); series.TransitionEasingFunction = new CubicEase {EasingMode = EasingMode.EaseOut}; this.DataChart.Series.Add(series);
Make sure that your data notifies the chart when it changes. For example, if your items bound to the chart implement the INotifyPropertyChanged interface, then modifying one of their properties will notify the chart, and the change will be animated. For a collection, if it implements the INotifyCollectionChanged interface, then additions, removals, and changes to the collection will also be animated.
The following code snippet shows an example of data that will notify the Data Chart when items are added, removed, replaced, and when their values are changed.
In Visual Basic:
Imports System.Collections.ObjectModel Imports System.ComponentModel ''' <summary> ''' Represents observable collection of data points ''' </summary> Public Class ObservableDataSource Inherits ObservableCollection(Of ObservableDataPoint) End Class ''' <summary> ''' Represents observable data point ''' </summary> 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.Collections.ObjectModel; using System.ComponentModel; /// <summary> /// Represents observable collection of data points /// </summary> public class ObservableDataSource : ObservableCollection<ObservableDataPoint> { } /// <summary> /// Represents observable data point /// </summary> 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 }
Ideally, you should update the data only as frequently as the TransitionDuration period. If you update the data more frequently, your chart will animate smoothly but will not be able to refresh to display the updated values: it will not be able to interpret them due to insufficient time to perform the necessary interpolations.
The following code shows how to use a timer to trigger updates in data animated in the XamDataChart.
In Visual Basic:
Imports System.Windows.Threading Imports Infragistics.Controls.Charts ' ... Protected ObservableDataSource As New ObservableDataSource() ' ... Private Sub Initialize() ' ... Me.Chart.Series(0).YMemberPath = "X" Me.Chart.Series(0).XMemberPath = "Y" Me.Chart.Series(0).ItemsSource = Me.DataSource ' ... Dim dataUpdateTimer As New DispatcherTimer() dataUpdateTimer.Interval = TimeSpan.FromSeconds(0.5) AddHandler dataUpdateTimer.Tick, AddressOf OnDataUpdateTimerTick dataUpdateTimer.Start() End Sub Private Sub OnDataUpdateTimerTick(sender As Object, e As EventArgs) Dim newDataPoint As New ObservableDataPoint() ' remove the first item Me.DataSource.RemoveAt(0) ' add the new item at the end Me.DataSource.Add(newDataPoint) End Sub
In C#:
using System.Windows.Threading; using Infragistics.Controls.Charts; // ... protected ObservableDataSource DataSource = new ObservableDataSource(); // ... private void Initialize() { // ... this.Chart.Series[0].YMemberPath = "X"; this.Chart.Series[0].XMemberPath = "Y"; this.Chart.Series[0].ItemsSource = this.DataSource; // ... DispatcherTimer dataUpdateTimer = new DispatcherTimer(); dataUpdateTimer.Interval = TimeSpan.FromSeconds(0.5); dataUpdateTimer.Tick += OnDataUpdateTimerTick; dataUpdateTimer.Start(); } private void OnDataUpdateTimerTick(object sender, EventArgs e) { ObservableDataPoint newDataPoint = new ObservableDataPoint(); this.DataSource.RemoveAt(0); // remove the first item this.DataSource.Add(newDataPoint); // add the new item at the end }
Another approach to using the Motion Framework in the XamDataChart is through our custom MotionFrameworkManager object which provides the following features:
Simpler configuration and interaction with the Motion Framework
Handling and synchronizing data updates for multiple data sources
Interactive experience through controls that start/stop data animation and display current state of the Motion Framework
Playback of data changes
You can download the Motion Framework Manager object and use it in your project. The following section explains how to use the manager for the Motion Framework.
The final result is shown in Figure 1 above.
Add a ToggleButton control and a Slider control to the root layout.
This button will control the animation playback of changes in data using the Motion Framework.
In XAML:
<Grid x:Name="LayoutRoot" > <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid Grid.Row="0" > <Grid.ColumnDefinitions> <ColumnDefinition Width="auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <ToggleButton x:Name="MotionPlayToggleButton" Grid.Column="0" > </ToggleButton> <Slider x:Name="MotionSlider" Grid.Column="1" Value="{Binding CurrentElementShown}" > </Slider> </Grid> <!--TODO: Add the XamDataChart control --> </Grid>
Add the XamDataChart control with horizontal and vertical axes:
In XAML:
<Grid x:Name="LayoutRoot" > <!-- ... --> <ig:XamDataChart x:Name="DataChart" HorizontalZoomable="True" HorizontalZoombarVisibility="Visible" VerticalZoomable="True" VerticalZoombarVisibility="Visible"> <ig:XamDataChart.Axes> <ig:NumericXAxis x:Name="axisX" > </ig:NumericXAxis> <ig:NumericYAxis x:Name="axisY" > </ig:NumericYAxis> </ig:XamDataChart.Axes> </ig:XamDataChart> </Grid>
Add the MotionFrameworkManager object to your project.
Declare MotionFrameworkManager and DataSources members.
In Visual Basic:
Imports System Imports System.Windows.Media.Animation Imports System.Collections.Generic Imports Infragistics.Controls.Charts Imports Infragistics.Samples.Common.Framework ' ... Protected MotionFrameworkManger As MotionFrameworkManger Protected DataSources As Dictionary(Of Integer, IList)
In C#:
using System; using System.Windows.Media.Animation; using System.Collections.Generic; using Infragistics.Controls.Charts; using Infragistics.Samples.Common.Framework; // ... protected MotionFrameworkManger MotionFrameworkManger; protected Dictionary<int, IList> DataSources;
Create an instance of the MotionFrameworkManager object in code-behind.
In Visual Basic:
Me.MotionFrameworkManger = New MotionFrameworkManger()
In C#:
this.MotionFrameworkManger = new MotionFrameworkManger();
Configure the MotionFrameworkManager object:
Set the TransitionFunction property to ExponentialEase animation function. **
Set the TransitionDuration property to 1 second. **
Set the DataUpdateInterval property to 1 second.
In Visual Basic:
Me.MotionFrameworkManger.TransitionFunction = New ExponentialEase() With { .EasingMode = EasingMode.EaseInOut } Me.MotionFrameworkManger.TransitionDuration = TimeSpan.FromMilliseconds(1000) Me.MotionFrameworkManger.DataUpdateInterval = TimeSpan.FromMilliseconds(1000)
In C#:
this.MotionFrameworkManger.TransitionFunction = new ExponentialEase { EasingMode = EasingMode.EaseInOut }; this.MotionFrameworkManger.TransitionDuration = TimeSpan.FromMilliseconds(1000); this.MotionFrameworkManger.DataUpdateInterval = TimeSpan.FromMilliseconds(1000);
Bind your own data source(s) that will be animated or use the DataSourceGenerator to generate random data.
In Visual Basic:
Me.DataSources = DataSourceGenerator.GetDataSources(8, 160) ' set data sources Me.MotionFrameworkManger.DataSources = Me.DataSources ' set the properties of bound objects to be used in the chart Me.MotionFrameworkManger.SeriesRadiusMemberPath = MotionDataPoint.PropertyNameValueR Me.MotionFrameworkManger.SeriesXMemberPath = MotionDataPoint.PropertyNameValueX Me.MotionFrameworkManger.SeriesYMemberPath = MotionDataPoint.PropertyNameValueY Me.MotionFrameworkManger.SeriesTimeMemberPath = MotionDataPoint.PropertyNameValueDateTime
In C#:
this.DataSources = DataSourceGenerator.GetDataSources(8, 160); // set data sources this.MotionFrameworkManger.DataSources = this.DataSources; // set the properties of bound objects to be used in the chart this.MotionFrameworkManger.SeriesRadiusMemberPath = MotionDataPoint.PropertyNameValueR; this.MotionFrameworkManger.SeriesXMemberPath = MotionDataPoint.PropertyNameValueX; this.MotionFrameworkManger.SeriesYMemberPath = MotionDataPoint.PropertyNameValueY; this.MotionFrameworkManger.SeriesTimeMemberPath = MotionDataPoint.PropertyNameValueDateTime;
Bind XamDataChart and map its axes to the manager of the Motion Framework.
In Visual Basic:
Me.MotionFrameworkManger.Chart = Me.DataChart Me.MotionFrameworkManger.XAxisName = "axisX" Me.MotionFrameworkManger.YAxisName = "axisY"
In C#:
this.MotionFrameworkManger.Chart = this.DataChart; this.MotionFrameworkManger.XAxisName = "axisX"; this.MotionFrameworkManger.YAxisName = "axisY";
Bind the Slider control created in the first step to the DateTimeSlider property of MotionFrameworkManager.
In Visual Basic:
Me.MotionFrameworkManger.DateTimeSlider = Me.MotionSlider
In C#:
this.MotionFrameworkManger.DateTimeSlider = this.MotionSlider;
.
Set the current DataContext to the MotionFrameworkManager object.
In Visual Basic:
Me.DataContext = Me.MotionFrameworkManger
In C#:
this.DataContext = this.MotionFrameworkManger;
.
Initialize the MotionFrameworkManager object.
In Visual Basic:
Me.MotionFrameworkManger.Initialize()
In C#:
this.MotionFrameworkManger.Initialize();
.
Add event handler for the Click event of the button that toggles play animation of the Motion Framework.
In Visual Basic:
Imports System.Windows ' ... AddHandler MotionPlayToggleButton.Click, AddressOf OnMotionPlayButtonClick ' ... Private Sub OnMotionPlayButtonClick(sender As Object, e As RoutedEventArgs) If Not Me.MotionFrameworkManger.IsPlaying Then Me.MotionFrameworkManger.Play() Me.MotionPlayToggleButton.IsChecked = True Else Me.MotionFrameworkManger.Pause() Me.MotionPlayToggleButton.IsChecked = False End If End Sub
In C#:
using System.Windows; // ... this.MotionPlayToggleButton.Click += OnMotionPlayButtonClick; // ... private void OnMotionPlayButtonClick(object sender, RoutedEventArgs e) { if (!this.MotionFrameworkManger.IsPlaying) { this.MotionFrameworkManger.Play(); this.MotionPlayToggleButton.IsChecked = true; } else { this.MotionFrameworkManger.Pause(); this.MotionPlayToggleButton.IsChecked = false; } }
.
Save your project.
.
(Optional) Verify the result. If you have implemented the code correctly, your animated chart should look like the one shown in Figure 1 above.