ChartDirector 6.2 (.NET Edition)

Simple Realtime Chart (Windows)




NOTE: This section describes Simple Realtime Chart for Windows applications only. For web applications, please refer to Simple Realtime Chart (Web).

NOTE: For conciseness, some of the following descriptions only mention WinChartViewer. Those descriptions apply to WPFChartViewer as well.

This sample program demonstrates a realtime chart with configurable chart update rate.

In this sample program, new values are generated by a random number generator driven by a timer. The values are "shifted" into data arrays, which are used for creating the chart.

The chart is updated by a second timer. This allows the chart update rate to be configurable independently from the data rate. Also, the chart can be "frozen" for easy reading, while the data can continue to update on the background.

To demonstrate the code structure for update rate control (even though for the update rate in this demo it is not necessary to have any rate control), instead of directly updating the chart, the chart update timer calls WinChartViewer.updateViewPort to trigger the WinChartViewer.ViewPortChanged event , and the chart is updated in its handler.

Source Code Listing

[Windows Forms - C# version] NetWinCharts\CSharpWinCharts\frmrealtimedemo.cs
using System;
using System.Windows.Forms;
using System.Collections;
using ChartDirector;

namespace CSharpChartExplorer
{
    public partial class FrmRealTimeDemo : Form
    {
        // The data arrays that store the visible data. The data arrays are updated in realtime. In
        // this demo, we plot the last 240 samples.
        private const int sampleSize = 240;
        private DateTime[] timeStamps = new DateTime[sampleSize];
        private double[] dataSeriesA = new double[sampleSize];
        private double[] dataSeriesB = new double[sampleSize];
        private double[] dataSeriesC = new double[sampleSize];

        // In this demo, we use a data generator driven by a timer to generate realtime data. The
        // nextDataTime is an internal variable used by the data generator to keep track of which
        // values to generate next.
        private DateTime nextDataTime = DateTime.Now;

        public FrmRealTimeDemo()
        {
            InitializeComponent();
        }

        private void FrmRealTimeDemo_Load(object sender, EventArgs e)
        {
            // Data generation rate
            dataRateTimer.Interval = 250;

            // Chart update rate, which can be different from the data generation rate.
            chartUpdateTimer.Interval = (int)samplePeriod.Value;

            // Initialize data buffer to no data.
            for (int i = 0; i < timeStamps.Length; ++i)
                timeStamps[i] = DateTime.MinValue;

            // Enable RunPB button
            runPB.Checked = true;

            // Now can start the timers for data collection and chart update
            dataRateTimer.Start();
            chartUpdateTimer.Start();
        }

        //
        // The data update routine. In this demo, it is invoked every 250ms to get new data.
        //
        private void dataRateTimer_Tick(object sender, EventArgs e)
        {
            do
            {
                //
                // In this demo, we use some formulas to generate new values. In real applications,
                // it may be replaced by some data acquisition code.
                //
                double p = nextDataTime.Ticks / 10000000.0 * 4;
                double dataA = 20 + Math.Cos(p * 2.2) * 10 + 1 / (Math.Cos(p) * Math.Cos(p) + 0.01);
                double dataB = 150 + 100 * Math.Sin(p / 27.7) * Math.Sin(p / 10.1);
                double dataC = 150 + 100 * Math.Cos(p / 6.7) * Math.Cos(p / 11.9);

                // After obtaining the new values, we need to update the data arrays.
                shiftData(dataSeriesA, dataA);
                shiftData(dataSeriesB, dataB);
                shiftData(dataSeriesC, dataC);
                shiftData(timeStamps, nextDataTime);

                // Update nextDataTime. This is needed by our data generator. In real applications,
                // you may not need this variable or the associated do/while loop.
                nextDataTime = nextDataTime.AddMilliseconds(dataRateTimer.Interval);
            }
            while (nextDataTime < DateTime.Now);

            // We provide some visual feedback to the numbers generated, so you can see the
            // values being generated.
            valueA.Text = dataSeriesA[dataSeriesA.Length - 1].ToString(".##");
            valueB.Text = dataSeriesB[dataSeriesB.Length - 1].ToString(".##");
            valueC.Text = dataSeriesC[dataSeriesC.Length - 1].ToString(".##");
        }

        //
        // Utility to shift a double value into an array
        //
        private void shiftData(double[] data, double newValue)
        {
            for (int i = 1; i < data.Length; ++i)
                data[i - 1] = data[i];
            data[data.Length - 1] = newValue;
        }

        //
        // Utility to shift a DataTime value into an array
        //
        private void shiftData(DateTime[] data, DateTime newValue)
        {
            for (int i = 1; i < data.Length; ++i)
                data[i - 1] = data[i];
            data[data.Length - 1] = newValue;
        }
        
        //
        // Enable/disable chart update based on the state of the Run button.
        //
        private void runPB_CheckedChanged(object sender, EventArgs e)
        {
            chartUpdateTimer.Enabled = runPB.Checked;
        }

        //
        // Updates the chartUpdateTimer interval if the user selects another interval.
        //
        private void samplePeriod_ValueChanged(object sender, EventArgs e)
        {
            chartUpdateTimer.Interval = (int)samplePeriod.Value;
        }

        //
        // The chartUpdateTimer Tick event - this updates the chart periodicially by raising
        // viewPortChanged events.
        //
        private void chartUpdateTimer_Tick(object sender, EventArgs e)
        {
            winChartViewer1.updateViewPort(true, false);
        }

        //
        // The viewPortChanged event handler. In this example, it just updates the chart. If you
        // have other controls to update, you may also put the update code here.
        //
        private void winChartViewer1_ViewPortChanged(object sender, WinViewPortEventArgs e)
        {
            drawChart(winChartViewer1);
        }

        //
        // Draw the chart and display it in the given viewer.
        //
        private void drawChart(WinChartViewer viewer)
        {
            // Create an XYChart object 600 x 270 pixels in size, with light grey (f4f4f4) 
            // background, black (000000) border, 1 pixel raised effect, and with a rounded frame.
            XYChart c = new XYChart(600, 270, 0xf4f4f4, 0x000000, 1);
            c.setRoundedFrame(Chart.CColor(BackColor));

            // Set the plotarea at (55, 62) and of size 520 x 175 pixels. Use white (ffffff) 
            // background. Enable both horizontal and vertical grids by setting their colors to 
            // grey (cccccc). Set clipping mode to clip the data lines to the plot area.
            c.setPlotArea(55, 62, 520, 175, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
            c.setClipping();

            // Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a light
            // grey (dddddd) background, black (000000) border, and a glass like raised effect.
            c.addTitle("Field Intensity at Observation Satellite", "Times New Roman Bold Italic", 15
                ).setBackground(0xdddddd, 0x000000, Chart.glassEffect());

            // Add a legend box at the top of the plot area with 9pts Arial Bold font. We set the 
            // legend box to the same width as the plot area and use grid layout (as opposed to 
            // flow or top/down layout). This distributes the 3 legend icons evenly on top of the 
            // plot area.
            LegendBox b = c.addLegend2(55, 33, 3, "Arial Bold", 9);
            b.setBackground(Chart.Transparent, Chart.Transparent);
            b.setWidth(520);

            // Configure the y-axis with a 10pts Arial Bold axis title
            c.yAxis().setTitle("Intensity (V/m)", "Arial Bold", 10);

            // Configure the x-axis to auto-scale with at least 75 pixels between major tick and 15 
            // pixels between minor ticks. This shows more minor grid lines on the chart.
            c.xAxis().setTickDensity(75, 15);

            // Set the axes width to 2 pixels
            c.xAxis().setWidth(2);
            c.yAxis().setWidth(2);

            // Now we add the data to the chart
            DateTime lastTime = timeStamps[timeStamps.Length - 1];
            if (lastTime != DateTime.MinValue)
            {
                // Set up the x-axis scale. In this demo, we set the x-axis to show the last 240 
                // samples, with 250ms per sample.
                c.xAxis().setDateScale(lastTime.AddSeconds(
                    -dataRateTimer.Interval * timeStamps.Length / 1000), lastTime);

                // Set the x-axis label format
                c.xAxis().setLabelFormat("{value|hh:nn:ss}");

                // Create a line layer to plot the lines
                LineLayer layer = c.addLineLayer2();

                // The x-coordinates are the timeStamps.
                layer.setXData(timeStamps);

                // The 3 data series are used to draw 3 lines. Here we put the latest data values
                // as part of the data set name, so you can see them updated in the legend box.
                layer.addDataSet(dataSeriesA, 0xff0000, "Alpha: <*bgColor=FFCCCC*>" +
                    c.formatValue(dataSeriesA[dataSeriesA.Length - 1], " {value|2} "));
                layer.addDataSet(dataSeriesB, 0x00cc00, "Beta: <*bgColor=CCFFCC*>" +
                    c.formatValue(dataSeriesB[dataSeriesB.Length - 1], " {value|2} "));
                layer.addDataSet(dataSeriesC, 0x0000ff, "Gamma: <*bgColor=CCCCFF*>" +
                    c.formatValue(dataSeriesC[dataSeriesC.Length - 1], " {value|2} "));

            }

            // Assign the chart to the WinChartViewer
            viewer.Chart = c;
        }
    }
}

[Windows Forms - VB Version] NetWinCharts\VBNetWinCharts\frmrealtimedemo.vb
Imports ChartDirector

Public Class FrmRealTimeDemo

    ' The data arrays that store the visible data. The data arrays are updated in realtime. In
    ' this demo, we plot the last 240 samples.
    Private Const sampleSize As Integer = 240
    Private dataSeriesA(sampleSize - 1) As Double
    Private dataSeriesB(sampleSize - 1) As Double
    Private dataSeriesC(sampleSize - 1) As Double
    Private timeStamps(sampleSize - 1) As Date

    ' In this demo, we use a data generator driven by a timer to generate realtime data. The
    ' nextDataTime is an internal variable used by the data generator to keep track of which
    ' values to generate next.
    Private nextDataTime As DateTime = DateTime.Now

    Private Sub FrmZoomScrollTrack_Load(ByVal sender As Object, ByVal e As EventArgs) _
        Handles MyBase.Load

        ' Data generation rate
        dataRateTimer.Interval = 250

        ' Chart update rate
        chartUpdateTimer.Interval = CInt(samplePeriod.Value)

        ' Initialize data buffer. In this demo, we just set the initial state to no data.
        Dim i As Integer
        For i = 0 To UBound(timeStamps)
            timeStamps(i) = DateTime.MinValue
        Next

        ' Enable RunPB button
        runPB.Checked = True

        ' Now can start the timers for data collection and chart update
        dataRateTimer.Start()
        chartUpdateTimer.Start()

    End Sub

    '/ <summary>
    '/ The data generator - invoke once every 250ms
    '/ </summary>
    Private Sub dataRateTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles dataRateTimer.Tick

        Do While nextDataTime < DateTime.Now
            '
            ' In this demo, we use some formulas to generate new values. In real applications,
            ' it may be replaced by some data acquisition code.
            '
            Dim p As Double = nextDataTime.Ticks / 10000000.0 * 4
            Dim dataA As Double = 20 + Math.Cos(p * 2.2) * 10 + 1 / (Math.Cos(p) * Math.Cos(p) + 0.01)
            Dim dataB As Double = 150 + 100 * Math.Sin(p / 27.7) * Math.Sin(p / 10.1)
            Dim dataC As Double = 150 + 100 * Math.Cos(p / 6.7) * Math.Cos(p / 11.9)

            ' We provide some visual feedback to the numbers generated, so you can see the
            ' values being generated.
            valueA.Text = dataA.ToString(".##")
            valueB.Text = dataB.ToString(".##")
            valueC.Text = dataC.ToString(".##")

            ' After obtaining the new values, we need to update the data arrays.
            shiftData(dataSeriesA, dataA)
            shiftData(dataSeriesB, dataB)
            shiftData(dataSeriesC, dataC)
            shiftData(timeStamps, nextDataTime)

            ' Update nextDataTime. This is needed by our data generator. In real applications,
            ' you may not need this variable or the associated do/while loop.
            nextDataTime = nextDataTime.AddMilliseconds(dataRateTimer.Interval)
        Loop

    End Sub

    '
    ' Utility to shift a value into an array
    '
    Private Sub shiftData(ByVal data As Object, ByVal newValue As Object)

        Dim i As Integer
        For i = 1 To UBound(data)
            data(i - 1) = data(i)
        Next
        data(UBound(data)) = newValue

    End Sub

    '
    ' Enable/disable chart update based on the state of the Run button.
    '
    Private Sub runPB_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles runPB.CheckedChanged

        chartUpdateTimer.Enabled = runPB.Checked

    End Sub

    '
    ' Updates the chartUpdateTimer interval if the user selects another interval.
    '
    Private Sub samplePeriod_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles samplePeriod.ValueChanged

        chartUpdateTimer.Interval = CInt(samplePeriod.Value)

    End Sub

    '
    ' The chartUpdateTimer Tick event - this updates the chart periodicially by raising
    ' viewPortChanged events.
    '
    Private Sub chartUpdateTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles chartUpdateTimer.Tick

        winChartViewer1.updateViewPort(True, False)

    End Sub

    '
    ' The viewPortChanged event handler. In this example, it just updates the chart. If you
    ' have other controls to update, you may also put the update code here.
    '
    Private Sub winChartViewer1_ViewPortChanged(ByVal sender As Object, ByVal e As WinViewPortEventArgs) _
        Handles winChartViewer1.ViewPortChanged

        drawChart(winChartViewer1)

    End Sub

    '
    ' Draw the chart and display it in the given viewer.
    '
    Private Sub drawChart(ByVal viewer As WinChartViewer)

        ' Create an XYChart object 600 x 270 pixels in size, with light grey (f4f4f4) 
        ' background, black (000000) border, 1 pixel raised effect, and with a rounded frame.
        Dim c As XYChart = New XYChart(600, 270, &HF4F4F4, &H000000, 1)
        c.setRoundedFrame(Chart.CColor(BackColor))

        ' Set the plotarea at (55, 62) and of size 520 x 175 pixels. Use white (ffffff) 
        ' background. Enable both horizontal and vertical grids by setting their colors to 
        ' grey (cccccc). Set clipping mode to clip the data lines to the plot area.
        c.setPlotArea(55, 62, 520, 175, &HFFFFFF, -1, -1, &HCCCCCC, &HCCCCCC)
        c.setClipping()

        ' Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a light
        ' grey (dddddd) background, black (000000) border, and a glass like raised effect.
        c.addTitle("Field Intensity at Observation Satellite", "Times New Roman Bold Italic", 15 _
            ).setBackground(&HDDDDDD, &H0, Chart.glassEffect())

        ' Add a legend box at the top of the plot area with 9pts Arial Bold font. We set the 
        ' legend box to the same width as the plot area and use grid layout (as opposed to 
        ' flow or top/down layout). This distributes the 3 legend icons evenly on top of the 
        ' plot area.
        Dim b As LegendBox = c.addLegend2(55, 33, 3, "Arial Bold", 9)
        b.setBackground(Chart.Transparent, Chart.Transparent)
        b.setWidth(520)

        ' Configure the y-axis with a 10pts Arial Bold axis title
        c.yAxis().setTitle("Intensity (V/m)", "Arial Bold", 10)

        ' Configure the x-axis to auto-scale with at least 75 pixels between major tick and 15 
        ' pixels between minor ticks. This shows more minor grid lines on the chart.
        c.xAxis().setTickDensity(75, 15)

        ' Set the axes width to 2 pixels
        c.xAxis().setWidth(2)
        c.yAxis().setWidth(2)

        ' Now we add the data to the chart
        Dim lastTime As DateTime = timeStamps(timeStamps.Length - 1)
        If lastTime <> DateTime.MinValue Then
            ' Set up the x-axis scale. In this demo, we set the x-axis to show the last 240 
            ' samples, with 250ms per sample.
            c.xAxis().setDateScale(lastTime.AddSeconds( _
                -dataRateTimer.Interval * timeStamps.Length / 1000), lastTime)

            ' Set the x-axis label format
            c.xAxis().setLabelFormat("{value|hh:nn:ss}")

            ' Create a line layer to plot the lines
            Dim layer As LineLayer = c.addLineLayer2()

            ' The x-coordinates are the timeStamps.
            layer.setXData(timeStamps)

            ' The 3 data series are used to draw 3 lines. Here we put the latest data values
            ' as part of the data set name, so you can see them updated in the legend box.
            layer.addDataSet(dataSeriesA, &HFF0000, "Alpha: <*bgColor=FFCCCC*>" & _
                c.formatValue(dataSeriesA(dataSeriesA.Length - 1), " {value|2} "))
            layer.addDataSet(dataSeriesB, &H00CC00, "Beta: <*bgColor=CCFFCC*>" & _
                c.formatValue(dataSeriesB(dataSeriesB.Length - 1), " {value|2} "))
            layer.addDataSet(dataSeriesC, &H0000FF, "Gamma: <*bgColor=CCCCFF*>" & _
                c.formatValue(dataSeriesC(dataSeriesC.Length - 1), " {value|2} "))
        End If

        ' Assign the chart to the WinChartViewer
        viewer.Chart = c

    End Sub

End Class

[WPF - XAML] NetWPFCharts\RealTimeDemoWindow.xaml
´╗┐<Window x:Class="CSharpWPFDemo.RealTimeDemoWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CSharpWPFDemo"
        mc:Ignorable="d"
        xmlns:ChartDirector="clr-namespace:ChartDirector;assembly=netchartdir" UseLayoutRounding="True"
        Title="Simple Realtime Chart" SizeToContent="WidthAndHeight" ResizeMode="NoResize" Loaded="Window_Loaded"
        >
    <DockPanel>
        <Label Content="Advanced Software Engineering" Height="25" DockPanel.Dock="Top" FontFamily="Arial" FontStyle="Italic" FontWeight="Bold" 
               FontSize="13" Background="#FF02098D" Foreground="#FFF4FF04" HorizontalContentAlignment="Right"/>
        <StackPanel DockPanel.Dock="Left" Width="120" Background="#FFF0F0F0">
            <RadioButton x:Name="runPB" Style="{StaticResource {x:Type ToggleButton}}" HorizontalContentAlignment="Left" 
                         Checked="runPB_CheckedChanged" Unchecked="runPB_CheckedChanged">
                <StackPanel Orientation="Horizontal" Margin="5">
                    <Image Source="runPB.gif" Width="16" Height="16" />
                    <TextBlock Text="Run Chart" Margin="6,0,0,0" />
                </StackPanel>
            </RadioButton>
            <RadioButton x:Name="freezePB" Style="{StaticResource {x:Type ToggleButton}}" HorizontalContentAlignment="Left">
                <StackPanel Orientation="Horizontal" Margin="5">
                    <Image Source="freezePB.gif" Width="16" Height="16" />
                    <TextBlock Text="Freeze Chart" Margin="6,0,0,0" />
                </StackPanel>
            </RadioButton>
            <TextBlock Text="Updated Rate (ms)" Margin="5,30,0,0" FontWeight="Bold" />
            <ComboBox x:Name="samplePeriod" Margin="3" SelectionChanged="samplePeriod_SelectionChanged">
                <ComboBoxItem>250</ComboBoxItem>
                <ComboBoxItem>500</ComboBoxItem>
                <ComboBoxItem>750</ComboBoxItem>
                <ComboBoxItem IsSelected="True">1000</ComboBoxItem>
                <ComboBoxItem>1250</ComboBoxItem>
                <ComboBoxItem>1500</ComboBoxItem>
                <ComboBoxItem>1750</ComboBoxItem>
                <ComboBoxItem>2000</ComboBoxItem>
            </ComboBox>
            <TextBlock Text="Simulated Machine" Margin="3,30,0,1" FontWeight="Bold" />
            <Grid Margin="3">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Alpha"/>
                <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Text="Beta"/>
                <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Text="Gamma"/>
                <Label x:Name="valueA" Grid.Row="0" Grid.Column="1" Content=" " Margin="6,0,0,0" Padding="2" BorderThickness="1" BorderBrush="Gray"/>
                <Label x:Name="valueB" Grid.Row="1" Grid.Column="1" Content=" " Margin="6,2,0,2" Padding="2" BorderThickness="1" BorderBrush="Gray"/>
                <Label x:Name="valueC" Grid.Row="2" Grid.Column="1" Content=" " Margin="6,0,0,0" Padding="2" BorderThickness="1" BorderBrush="Gray"/>
            </Grid>
        </StackPanel>
        <ChartDirector:WPFChartViewer x:Name="WPFChartViewer1" Width="600" Height="270" Margin="5" ViewPortChanged="WPFChartViewer1_ViewPortChanged" />
    </DockPanel>
</Window>

[WPF - C#] NetWPFCharts\RealTimeDemoWindow.xaml.cs
´╗┐using System;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Controls;
using ChartDirector;

namespace CSharpWPFDemo
{
    public partial class RealTimeDemoWindow : Window
    {
        // The data arrays that store the visible data. The data arrays are updated in realtime. In
        // this demo, we plot the last 240 samples.
        private const int sampleSize = 240;
        private DateTime[] timeStamps = new DateTime[sampleSize];
        private double[] dataSeriesA = new double[sampleSize];
        private double[] dataSeriesB = new double[sampleSize];
        private double[] dataSeriesC = new double[sampleSize];

        // In this demo, we use a data generator driven by a timer to generate realtime data. The
        // nextDataTime is an internal variable used by the data generator to keep track of which
        // values to generate next.
        private DispatcherTimer dataRateTimer = new DispatcherTimer(DispatcherPriority.Render);
        private DateTime nextDataTime = DateTime.Now;

        // Timer used to updated the chart
        private DispatcherTimer chartUpdateTimer = new DispatcherTimer(DispatcherPriority.Render);

        public RealTimeDemoWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Data generation rate = 250ms
            dataRateTimer.Interval = new TimeSpan(0, 0, 0, 0, 250);
            dataRateTimer.Tick += dataRateTimer_Tick;

            // Chart update rate, which can be different from the data generation rate.
            chartUpdateTimer.Interval = new TimeSpan(0, 0, 0, 0, int.Parse(samplePeriod.Text));
            chartUpdateTimer.Tick += chartUpdateTimer_Tick;

            // Initialize data buffer to no data.
            for (int i = 0; i < timeStamps.Length; ++i)
                timeStamps[i] = DateTime.MinValue;

            // Enable RunPB button
            runPB.IsChecked = true;

            // Now can start the timers for data collection and chart update
            dataRateTimer.Start();
            chartUpdateTimer.Start();
        }

        //
        // The data update routine. In this demo, it is invoked every 250ms to get new data.
        //
        private void dataRateTimer_Tick(object sender, EventArgs e)
        {
            do
            {
                //
                // In this demo, we use some formulas to generate new values. In real applications,
                // it may be replaced by some data acquisition code.
                //
                double p = nextDataTime.Ticks / 10000000.0 * 4;
                double dataA = 20 + Math.Cos(p * 2.2) * 10 + 1 / (Math.Cos(p) * Math.Cos(p) + 0.01);
                double dataB = 150 + 100 * Math.Sin(p / 27.7) * Math.Sin(p / 10.1);
                double dataC = 150 + 100 * Math.Cos(p / 6.7) * Math.Cos(p / 11.9);

                // After obtaining the new values, we need to update the data arrays.
                shiftData(dataSeriesA, dataA);
                shiftData(dataSeriesB, dataB);
                shiftData(dataSeriesC, dataC);
                shiftData(timeStamps, nextDataTime);

                // Update nextDataTime. This is needed by our data generator. In real applications,
                // you may not need this variable or the associated do/while loop.
                nextDataTime = nextDataTime.AddMilliseconds(dataRateTimer.Interval.TotalMilliseconds);
            }
            while (nextDataTime < DateTime.Now);

            // We provide some visual feedback to the numbers generated, so you can see the
            // values being generated.
            valueA.Content = dataSeriesA[dataSeriesA.Length - 1].ToString(".##");
            valueB.Content = dataSeriesB[dataSeriesB.Length - 1].ToString(".##");
            valueC.Content = dataSeriesC[dataSeriesC.Length - 1].ToString(".##");
        }

        //
        // Utility to shift a double value into an array
        //
        private void shiftData(double[] data, double newValue)
        {
            for (int i = 1; i < data.Length; ++i)
                data[i - 1] = data[i];
            data[data.Length - 1] = newValue;
        }

        //
        // Utility to shift a DataTime value into an array
        //
        private void shiftData(DateTime[] data, DateTime newValue)
        {
            for (int i = 1; i < data.Length; ++i)
                data[i - 1] = data[i];
            data[data.Length - 1] = newValue;
        }

        //
        // Enable/disable chart update based on the state of the Run button.
        //
        private void runPB_CheckedChanged(object sender, RoutedEventArgs e)
        {
            chartUpdateTimer.IsEnabled = runPB.IsChecked == true;
        }

        //
        // Updates the chartUpdateTimer interval if the user selects another interval.
        //

        private void samplePeriod_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selectedText = (samplePeriod.SelectedValue as ComboBoxItem).Content as string;
            if (!string.IsNullOrEmpty(selectedText))
                chartUpdateTimer.Interval = new TimeSpan(0, 0, 0, 0, int.Parse(selectedText));
        }

        //
        // The chartUpdateTimer Tick event - this updates the chart periodicially by raising
        // viewPortChanged events.
        //
        private void chartUpdateTimer_Tick(object sender, EventArgs e)
        {
            WPFChartViewer1.updateViewPort(true, false);
        }

        //
        // The viewPortChanged event handler. In this example, it just updates the chart. If you
        // have other controls to update, you may also put the update code here.
        //
        private void WPFChartViewer1_ViewPortChanged(object sender, WPFViewPortEventArgs e)
        {
            drawChart(sender as WPFChartViewer);
        }

        //
        // Draw the chart and display it in the given viewer.
        //
        private void drawChart(WPFChartViewer viewer)
        {
            // Create an XYChart object 600 x 270 pixels in size, with light grey (f4f4f4) 
            // background, black (000000) border, 1 pixel raised effect, and with a rounded frame.
            XYChart c = new XYChart(600, 270, 0xf4f4f4, 0x000000, 1);
            c.setRoundedFrame(0xffffff);

            // Set the plotarea at (55, 62) and of size 520 x 175 pixels. Use white (ffffff) 
            // background. Enable both horizontal and vertical grids by setting their colors to 
            // grey (cccccc). Set clipping mode to clip the data lines to the plot area.
            c.setPlotArea(55, 62, 520, 175, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
            c.setClipping();

            // Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a light
            // grey (dddddd) background, black (000000) border, and a glass like raised effect.
            c.addTitle("Field Intensity at Observation Satellite", "Times New Roman Bold Italic", 15
                ).setBackground(0xdddddd, 0x000000, Chart.glassEffect());

            // Add a legend box at the top of the plot area with 9pts Arial Bold font. We set the 
            // legend box to the same width as the plot area and use grid layout (as opposed to 
            // flow or top/down layout). This distributes the 3 legend icons evenly on top of the 
            // plot area.
            LegendBox b = c.addLegend2(55, 33, 3, "Arial Bold", 9);
            b.setBackground(Chart.Transparent, Chart.Transparent);
            b.setWidth(520);

            // Configure the y-axis with a 10pts Arial Bold axis title
            c.yAxis().setTitle("Intensity (V/m)", "Arial Bold", 10);

            // Configure the x-axis to auto-scale with at least 75 pixels between major tick and 15 
            // pixels between minor ticks. This shows more minor grid lines on the chart.
            c.xAxis().setTickDensity(75, 15);

            // Set the axes width to 2 pixels
            c.xAxis().setWidth(2);
            c.yAxis().setWidth(2);

            // Now we add the data to the chart
            DateTime lastTime = timeStamps[timeStamps.Length - 1];
            if (lastTime != DateTime.MinValue)
            {
                // Set up the x-axis scale. In this demo, we set the x-axis to show the last 240 
                // samples, with 250ms per sample.
                c.xAxis().setDateScale(lastTime.AddSeconds(
                    -dataRateTimer.Interval.TotalSeconds * timeStamps.Length), lastTime);

                // Set the x-axis label format
                c.xAxis().setLabelFormat("{value|hh:nn:ss}");

                // Create a line layer to plot the lines
                LineLayer layer = c.addLineLayer2();

                // The x-coordinates are the timeStamps.
                layer.setXData(timeStamps);

                // The 3 data series are used to draw 3 lines. Here we put the latest data values
                // as part of the data set name, so you can see them updated in the legend box.
                layer.addDataSet(dataSeriesA, 0xff0000, "Alpha: <*bgColor=FFCCCC*>" +
                    c.formatValue(dataSeriesA[dataSeriesA.Length - 1], " {value|2} "));
                layer.addDataSet(dataSeriesB, 0x00cc00, "Beta: <*bgColor=CCFFCC*>" +
                    c.formatValue(dataSeriesB[dataSeriesB.Length - 1], " {value|2} "));
                layer.addDataSet(dataSeriesC, 0x0000ff, "Gamma: <*bgColor=CCCCFF*>" +
                    c.formatValue(dataSeriesC[dataSeriesC.Length - 1], " {value|2} "));

            }

            // Assign the chart to the WinChartViewer
            viewer.Chart = c;
        }
    }
}