All docs
V20.1
20.1
19.2
The page you are viewing does not exist in version 19.2. This link will take you to the root page.
19.1
The page you are viewing does not exist in version 19.1. This link will take you to the root page.
18.2
The page you are viewing does not exist in version 18.2. This link will take you to the root page.
18.1
The page you are viewing does not exist in version 18.1. This link will take you to the root page.
17.2
The page you are viewing does not exist in version 17.2. This link will take you to the root page.

How to: Create a Real-Time Chart

  • 6 minutes to read

This topic explains how to create a chart and add new points to its data source in real time.

The chart processes points that are within its viewport. In the following examples, points that are beyond the viewport are removed from the data source - starting from the beginning of the collection.

Use a Single Thread to Create a Real-Time Chart

The following code generates a new data point that is added to the chart each time the Timer.Tick event occurs (every 100 milliseconds). The example uses an ObservableCollection as the data source for a series. ObservableCollection notifies the chart about new items, and the chart is rendered again.

NOTE

To see the complete sample project, refer to the following GitHub example: How to: Create a Real-Time Chart.

using DevExpress.Utils;
using DevExpress.XtraCharts;
using System;
using System.Collections.ObjectModel;
using System.Windows.Forms;

namespace RealTimeChartUpdates {
    public partial class Form1 : Form {
        const int ViewportPointCount = 100;
        ObservableCollection<DataPoint> dataPoints = new ObservableCollection<DataPoint>();
        public Form1() { InitializeComponent(); }

        void Form1_Load(object sender, EventArgs e) {
            chartControl1.Titles.Add(new ChartTitle { Text = "Real-Time Charting" });

            Series series = new Series();
            series.ChangeView(ViewType.Line);
            series.DataSource = dataPoints;
            series.DataSourceSorted = true;
            series.ArgumentDataMember = "Argument";
            series.ValueDataMembers.AddRange("Value");
            chartControl1.Series.Add(series);

            LineSeriesView seriesView = (LineSeriesView)series.View;
            seriesView.LastPoint.LabelDisplayMode = SidePointDisplayMode.DiagramEdge;
            seriesView.LastPoint.Label.TextPattern = "{V:f2}";

            XYDiagram diagram = (XYDiagram)chartControl1.Diagram;
            diagram.AxisX.DateTimeScaleOptions.ScaleMode = ScaleMode.Continuous;
            diagram.AxisX.Label.ResolveOverlappingOptions.AllowRotate = false;
            diagram.AxisX.Label.ResolveOverlappingOptions.AllowStagger = false;
            diagram.AxisX.WholeRange.SideMarginsValue = 0;
            diagram.DependentAxesYRange = DefaultBoolean.True;
            diagram.AxisY.WholeRange.AlwaysShowZeroLevel = false;

            Timer timer = new Timer();
            timer.Interval = 100;
            timer.Start();
            timer.Tick += Timer_Tick;
        }
        int counter = 0;
        void Timer_Tick(object sender, EventArgs e) {
            dataPoints.Add(new DataPoint(DateTime.Now, GenerateValue(counter++)));
            if (dataPoints.Count > ViewportPointCount)
                dataPoints.RemoveAt(0);
        }
        double GenerateValue(double x) { 
            return Math.Sin(x) * 3 + x / 2 + 5; 
        }
    }
    public class DataPoint {
        public DateTime Argument { get; set; }
        public double Value { get; set; }
        public DataPoint(DateTime argument, double value) {
            Argument = argument;
            Value = value;
        }
    }
}

Collect Data in a Separate Thread

The following example uses a separate thread to accumulate data points. A new batch of data points is generated every 15 milliseconds. The chart fetches a new portion of points to visualize at a rate of ten times per second.

NOTE

To see the complete sample project, refer to the following GitHub example: How to: Create a Real-Time Chart and Collect Data in a Separate Thread.

using DevExpress.Utils;
using DevExpress.XtraCharts;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace RealTimeChartUpdates {
    public partial class Form1 : Form {
        const int ViewportPointCount = 5000;
        int counter = 0;
        Thread dataAcquisitionThread;
        bool isEnabled = true;
        int lastRawDataIndex = 0;
        List<DataPoint> rawData = new List<DataPoint>();
        ObservableCollection<DataPoint> viewportData = new ObservableCollection<DataPoint>();
        public Form1() { InitializeComponent(); }
        void Form1_Load(object sender, EventArgs e) {
            dataAcquisitionThread = new Thread(new ThreadStart(AcquireData));
            dataAcquisitionThread.Start();

            chartControl1.Titles.Add(new ChartTitle { Text = "Real-Time Charting" });

            Series series = new Series();
            series.ChangeView(ViewType.Line);
            series.DataSource = viewportData;
            series.DataSourceSorted = true;
            series.ArgumentDataMember = "Argument";
            series.ValueDataMembers.AddRange("Value");
            chartControl1.Series.Add(series);

            LineSeriesView seriesView = (LineSeriesView)series.View;
            seriesView.LastPoint.LabelDisplayMode = SidePointDisplayMode.SeriesPoint;
            seriesView.LastPoint.Label.TextPattern = "{V:f2}";

            XYDiagram diagram = (XYDiagram)chartControl1.Diagram;
            diagram.AxisX.DateTimeScaleOptions.ScaleMode = ScaleMode.Continuous;
            diagram.AxisX.Label.ResolveOverlappingOptions.AllowRotate = false;
            diagram.AxisX.Label.ResolveOverlappingOptions.AllowStagger = false;
            diagram.AxisX.VisualRange.EndSideMargin = 200;
            diagram.DependentAxesYRange = DefaultBoolean.True;
            diagram.AxisY.WholeRange.AlwaysShowZeroLevel = false;

            System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
            timer.Interval = 100;
            timer.Start();
            timer.Tick += Timer_Tick;
        }
        void AcquireData() {
            while (isEnabled) {
                Thread.Sleep(15);
                lock (rawData) {
                    for (int i = 0; i < 50; i++)
                        rawData.Add(new DataPoint(DateTime.Now, GenerateValue(counter++)));
                }
            }
        }
        double GenerateValue(double x) { return Math.Sin(x / 1000.0) * 3 * x + x / 2 + 5; }
        void Timer_Tick(object sender, EventArgs e) {
            lock (rawData) {
                for (int i = Math.Max(lastRawDataIndex, rawData.Count - ViewportPointCount); i < rawData.Count; i++)
                    viewportData.Add(rawData[i]);
                lastRawDataIndex = rawData.Count;
                while (viewportData.Count > ViewportPointCount)
                    viewportData.RemoveAt(0);
            }
        }
        protected override void OnClosing(CancelEventArgs e) {
            isEnabled = false;
            base.OnClosing(e);
        }
        public class DataPoint {
            public DataPoint(DateTime argument, double value) {
                Argument = argument;
                Value = value;
            }
            public DateTime Argument { get; set; }
            public double Value { get; set; }
        }
    }
}
See Also