SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, iOS Chart, Android Chart and JavaScript Chart Components
SciChart WPF ships with hundreds of WPF Chart Examples which you can browse, play with, view the source-code and even export each WPF Chart Example to a stand-alone Visual Studio solution. All of this is possible with the new and improved SciChart WPF Examples Suite, which ships as part of the SciChart WPF SDK.
Demonstrates the difference between FIFO and standard DataSeries. A FIFO series discards old data-points once a predefined number of points is met. This type of data-series is extremely efficient for scrolling charts, where you do not care about discarded (off-screen) data.
FIFO Series can be used in more ways than just scrolling a chart. For instance, reload the entire FIFO buffer each frame to get a Spectrum Analyzer style chart. Or, use FIFO series and set the XAxis.VisibleRange to achieve an ECG style chart.
Example Usage
If you pause this example you can uncheck USE FIFO and re-run it to see the difference in behavior. At any time, press Reset to get back to the starting state.
Documentation Links
– DataSeries.FifoCapacity property
– Performance Tips and Tricks
The C#/WPF source code for the WPF Realtime Scrolling Charts with FIFO example is included below (Scroll down!).
Did you know you can also view the source code from one of the following sources as well?
<UserControl x:Class="SciChart.Examples.Examples.CreateRealtimeChart.RealtimeFifoChartView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
Loaded="OnExampleLoaded"
Unloaded="OnExampleUnloaded"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ext="http://schemas.abtsoftware.co.uk/scichart/exampleExternals"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SciChart.Examples.ExternalDependencies;component/Resources/Styles/MergedResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="#FF232426" BorderThickness="0,0,2,0" BorderBrush="#323539" Canvas.ZIndex="9999">
<StackPanel Orientation="Vertical">
<ToggleButton x:Name="StartButton" Click="StartButton_Click" Style="{StaticResource PlayButtonStyle}" Padding="4"/>
<ToggleButton x:Name="PauseButton" Click="PauseButton_Click" Style="{StaticResource PauseButtonStyle}" Padding="7"/>
<ToggleButton x:Name="ResetButton" Click="ResetButton_Click" Style="{StaticResource StopButtonStyle}" Padding="7"/>
<ext:FlyoutMenuButton Style="{StaticResource FlyoutMenuButtonStyle}" Content="FIFO" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock Text="Additional Control" />
<ext:FlyoutSeparator/>
<CheckBox x:Name="IsFifoCheckBox" Margin="4,4,4,8" Click="OnIsFifoSeriesChanged" Content="Use Fifo?" IsChecked="True" />
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
</StackPanel>
</Border>
<!-- Define the SciChartSurface -->
<s:SciChartSurface x:Name="sciChart"
Grid.Column="1"
>
<!-- Create three RenderableSeries, which map 1:1 to the DataSeries created in code-behind -->
<s:SciChartSurface.RenderableSeries>
<s:FastLineRenderableSeries x:Name="RenderableSeries0"
Stroke="#FFE13219"
StrokeThickness="3" />
<s:FastLineRenderableSeries x:Name="RenderableSeries1"
Stroke="#FFFFA500"
StrokeThickness="3" />
<s:FastLineRenderableSeries x:Name="RenderableSeries2"
Stroke="#FF4083B7"
StrokeThickness="3" />
</s:SciChartSurface.RenderableSeries>
<!-- Create an X Axis -->
<s:SciChartSurface.XAxis>
<s:NumericAxis MinHeight="50"
AutoRange="Always"
AxisTitle="Time (Sec)"
DrawMinorGridLines="False"
DrawMinorTicks="False"
TextFormatting="0.00">
<s:NumericAxis.GrowBy>
<s:DoubleRange Max="0.1" Min="0.1" />
</s:NumericAxis.GrowBy>
</s:NumericAxis>
</s:SciChartSurface.XAxis>
<!-- Create a Y Axis -->
<s:SciChartSurface.YAxis>
<s:NumericAxis AutoRange="Always"
AxisTitle="Value"
DrawMinorGridLines="False"
DrawMinorTicks="False"
TextFormatting="0.00">
<s:NumericAxis.GrowBy>
<s:DoubleRange Max="0.1" Min="0.1" />
</s:NumericAxis.GrowBy>
</s:NumericAxis>
</s:SciChartSurface.YAxis>
</s:SciChartSurface>
</Grid>
</UserControl>
// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2022. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales: sales@scichart.com
//
// RealtimeFifoChartView.xaml.cs is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
// *************************************************************************************
using System;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using SciChart.Charting.Model.DataSeries;
using SciChart.Core.Utility;
namespace SciChart.Examples.Examples.CreateRealtimeChart
{
public partial class RealtimeFifoChartView : UserControl
{
// Data Sample Rate (sec) - 20 Hz
private const double dt = 0.02;
// FIFO Size is 200 samples, meaning after 200 samples have been appended, each new sample appended
// results in one sample being discarded
private const int FifoSize = 200;
// Timer to process updates
private readonly Timer _timerNewDataUpdate;
// The current time
private double t = -5d;
private readonly Random _random = new Random();
// The dataseries to fill
private IUniformXyDataSeries<double> series0;
private IUniformXyDataSeries<double> series1;
private IUniformXyDataSeries<double> series2;
private TimedMethod _startDelegate;
public RealtimeFifoChartView()
{
InitializeComponent();
_timerNewDataUpdate = new Timer(dt * 1000) { AutoReset = true };
_timerNewDataUpdate.Elapsed += OnNewData;
CreateDataSetAndSeries();
}
private void CreateDataSetAndSeries()
{
// Create new Dataseries of type X=double, Y=double
series0 = new UniformXyDataSeries<double>(t, dt);
series1 = new UniformXyDataSeries<double>(t, dt);
series2 = new UniformXyDataSeries<double>(t, dt);
if (IsFifoCheckBox.IsChecked == true)
{
// Add three FIFO series to fill with data.
// setting the FIFO capacity will denote this series as a FIFO series. New data is appended until the size is met, at which point
// old data is discarded. Internally the FIFO series is implemented as a circular buffer so that old data is pushed out of the buffer
// once the capacity has been reached
// Note: Once a FIFO series has been added to a dataset, all subsequent series must be FIFO series. In addition, the FifoSize must be the
// same for all FIFO series in a dataset.
series0.FifoCapacity = FifoSize;
series1.FifoCapacity = FifoSize;
series2.FifoCapacity = FifoSize;
}
// Set the dataseries on the chart's RenderableSeries
RenderableSeries0.DataSeries = series0;
RenderableSeries1.DataSeries = series1;
RenderableSeries2.DataSeries = series2;
}
private void ClearDataSeries()
{
using (sciChart.SuspendUpdates())
{
series0?.Clear();
series1?.Clear();
series2?.Clear();
}
}
private void OnNewData(object sender, EventArgs e)
{
// Compute our three series values
double y1 = 3.0 * Math.Sin(2 * Math.PI * 1.4 * t) + _random.NextDouble() * 0.5;
double y2 = 2.0 * Math.Cos(2 * Math.PI * 0.8 * t) + _random.NextDouble() * 0.5;
double y3 = 1.0 * Math.Sin(2 * Math.PI * 2.2 * t) + _random.NextDouble() * 0.5;
// Suspending updates is optional, and ensures we only get one redraw
// once all three dataseries have been appended to
using (sciChart.SuspendUpdates())
{
// Append x,y data to previously created series
series0.Append(y1);
series1.Append(y2);
series2.Append(y3);
}
// Increment current time
t += dt;
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
StartButton.IsChecked = true;
PauseButton.IsChecked = false;
ResetButton.IsChecked = false;
IsFifoCheckBox.IsEnabled = false;
// Start a timer to create new data and append on each tick
_timerNewDataUpdate.Start();
}
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
_timerNewDataUpdate?.Stop();
StartButton.IsChecked = false;
PauseButton.IsChecked = true;
ResetButton.IsChecked = false;
IsFifoCheckBox.IsEnabled = false;
}
private void ResetButton_Click(object sender, RoutedEventArgs e)
{
PauseButton_Click(this, null);
StartButton.IsChecked = false;
PauseButton.IsChecked = false;
ResetButton.IsChecked = true;
IsFifoCheckBox.IsEnabled = true;
t = -5d;
ClearDataSeries();
}
private void OnIsFifoSeriesChanged(object sender, RoutedEventArgs e)
{
CreateDataSetAndSeries();
}
private void OnExampleLoaded(object sender, RoutedEventArgs e)
{
ResetButton_Click(this, null);
_startDelegate = TimedMethod.Invoke(() => StartButton_Click(this, null)).After(500).Go();
}
private void OnExampleUnloaded(object sender, RoutedEventArgs e)
{
if (_startDelegate != null)
{
_startDelegate.Dispose();
_startDelegate = null;
}
PauseButton_Click(this, null);
}
}
}