
.NET Data VisualizationFast Line Charts
ArticleReviewing .NET Fast Line applications VS LightningChart .NET Fast Line Series.
Written by a human | Updated on April 14th, 2025
Fast Line Charts
Sooner or later, when working on a data visualization task, one can come to the realization that a ‘fast line chart’ is needed. It is an ambiguous term that could mean a great number of things. If a software developer is programming with C#, the first thing that comes to mind is the .NET Chart class with FastLine type series.
However, it is more likely that under the term ‘fast line chart’ a user would like to stress the word ‘fast’. As we will see in this article, the name does not imply that Microsoft .NET Chart FastLine will provide the best performance – at least not nowadays. Currently, there are faster line charts and we will discuss the fastest of all: LightningChart .NET control.
Download the project to follow the tutorial
.NET Framework and Data Visualization
The .NET Framework was introduced in 2002 and starting from version 4.0 (year 2010), the Chart class could be found right inside of API. It should be very easy to get started displaying data in WinForms applications as it is natively available and automatically appears in the Windows Forms Toolbox in Visual Studio. So, the user just needs to drag a Chart from the toolbox (Data->Chart) and drop it onto its own form (see figure 1).
Figure 1. Dragging .NET Chart control from Visual Studio toolbox to Windows Forms App (.NET Framework) project’s Form.
After we added a chart in the application, we should next generate/read some data for it. We could just add random points and the job would be done but, where’s the fun in that?
Let’s make an example a little bit more interactive: allow settings various points count, test the chart loading and data-streaming capabilities. We will add TextBox and 3 Buttons to Form1. For example:
this.dotNETchart = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.btnCreateFastLine = new System.Windows.Forms.Button();
this.btnClear = new System.Windows.Forms.Button();
this.btnAddPoints = new System.Windows.Forms.Button();
this.txtPointCount = new System.Windows.Forms.TextBox();
First, we just simply want to add some Create 2 Series. One contains a sawtooth signal and another is a sinusoidal signal. Below is the code that generates such data with a period of 10:
iPointCount = Convert.ToInt32(txtPointCount.Text); // visible Point count, pre-generated.
signalPeriod = iPointCount / 10;
double xMax = 100;
_xStep = xMax / (iPointCount - 1);
// generate sawtooth-signal
double[] xValues1 = new double[iPointCount];
double[] yValues1 = new double[iPointCount];
for (int i = 0; i < iPointCount; i++)
{
xValues1[i] = (double)i * _xStep;
yValues1[i] = 20 * Math.Abs(Math.Round((double)i / signalPeriod) - (double)i / signalPeriod );
}
_lastXValue = xValues1[iPointCount - 1];
// generate sinusoidal-signal
double[] xValues2 = new double[iPointCount];
double[] yValues2 = new double[iPointCount];
for (int i = 0; i < iPointCount; i++)
{
xValues2[i] = (double)i * xMax / (iPointCount - 1);
yValues2[i] = 8.0 * Math.Sin(2 * Math.PI * (double)i / signalPeriod );
}
We added not only Y-values but also an X-values array to make it more general. Now it is time to create a series and add data to it.
// create series
Series series1 = new Series("Series A");
series1.Points.DataBindXY(xValues1, yValues1);
series1.ChartType = SeriesChartType.FastLine;
series1.IsVisibleInLegend = false;
Series series2 = new Series("Series B");
series2.Points.DataBindXY(xValues2, yValues2);
series2.ChartType = SeriesChartType.FastLine;
series2.IsVisibleInLegend = false;
Of course, a series should be added to our chart. We can do that by adding just few simple lines of code:
// add series to the chart
dotNETchart.Series.Clear();
dotNETchart.Series.Add(series1);
dotNETchart.Series.Add(series2);
At the end, we can add some Chart styling and formatting:
// some Chart styling
dotNETchart.ResetAutoValues();
dotNETchart.Titles.Clear();
dotNETchart.Titles.Add($".NET FastLine Chart ({iPointCount:N0} points per series)");
dotNETchart.ChartAreas[0].AxisX.Title = "Horizontal Axis Label";
dotNETchart.ChartAreas[0].AxisY.Title = "Vertical Axis Label";
dotNETchart.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.LightGray;
dotNETchart.ChartAreas[0].AxisX.MajorGrid.LineColor = Color.LightGray;
dotNETchart.ChartAreas[0].AxisX.LabelStyle.Format = "0";
And here is the resulting figure/chart:
Figure 2. FastLine type series in .NET Chart control.
Overall FastLine in .NET Chart control is optimized for speed (as compared to for example SeriesChartType.Line). However, the performance of this ‘fast line chart’ gets very sluggish if the point count is more than 1 million data points.
For example, on my reasonable modern computer, it takes a few seconds to draw the chart with 2 x 1M points and it takes around 3 min to draw 2 x 10 M points. Readers can test themselves with this test application. Just clear data (clicking the ‘Clear’ button), enter the new count in the box and hit the ‘Create Fast Line’ button.
As you will see later in the article, there are far better and faster Charts in the market. LightningChart can display 2 x 1M points in the blink of an eye. Even more, 2 x 100 M points could be loaded to the chart in 7 sec, while 2 x 500 M points loaded to the chart in just under the minute, that is a huge difference!
Summary of test results are in Table 1. Based only on the data loading speed there is already a clear winner when trying to define a fast line chart. However, let’s look at other features.
Table 1. Chart initialization/drawing tests (time to show a specific amount of points).
Data-streaming logic could be implemented in various ways. One way is to use Timer Ticks event handler to add new points several times per second. But that approach is not optimal as it would keep UI Thread busy and may freeze data-streaming if the chart update speed is not sufficient.
Another approach, which we use in this application, is to use a background thread for data generation. Clicking on the ‘Add points’ button will create a thread that executes a method (ThreadLoop):
private void btnAddPoints_Click(object sender, EventArgs e)
{
if (_thread == null)
{
_stop = false;
_thread = new Thread(new ThreadStart(ThreadLoop));
_thread.Start();
}
}
We want a ThreadLoop method with an infinite loop of data generation (also known as data gathering), unless the background thread is stopped or the application is closed.
For the purpose of data-streaming performance testing, we will be aiming to add 1/1000th of the total Points count every chart update cycle.
New points are added at the end of the collection with the chart.Series[0].Points.AddXY(xValues, yValues1). This is a method and scrolling realized by modifying AxisX Maximum and Minimum properties accordingly.
private void ThreadLoop()
{
int iNewPointCount = iPointCount / 1000;
double xValues = 0;
double yValues1, yValues2;
while (_stop == false)
{
_invokeInProgress = true; // let the form know, if an invoke has started
dotNETchart.Invoke(new Action(() => // update Chart in UI Thread
{
for (int iNew = 0; iNew < iNewPointCount; iNew++)
{
xValues = _lastXValue + (double)(iNew + 1) * _xStep;
yValues1 = 20 * Math.Abs(Math.Round((double)(iPointCount + iNew) / signalPeriod) - (double)(iPointCount + iNew) / signalPeriod);
dotNETchart.Series[0].Points.AddXY(xValues, yValues1);
yValues2 = 8.0 * Math.Sin(2 * Math.PI * (double)(iPointCount + iNew) / signalPeriod);
dotNETchart.Series[1].Points.AddXY(xValues, yValues2);
}
_lastXValue = xValues;
dotNETchart.ChartAreas[0].AxisX.Maximum = _lastXValue;
dotNETchart.ChartAreas[0].AxisX.Minimum = _lastXValue - 100;
iPointCount = iPointCount + iNewPointCount;
dotNETchart.Titles.Clear();
dotNETchart.Titles.Add($".NET FastLine Chart ({iPointCount:N0} points per series)");
}));
_invokeInProgress = false; // the invoke is complete
// Sleep a little bit so that UI stays responsive.
Thread.Sleep(1);
}
_thread = null;
}
While the multithreaded chart application helps to keep the application’s UI responsive and fast, it does require careful design and handling of threads. We covered principles and examples in the Multithreaded Chart Applications blog article.
Firstly, when using a background thread in the application, all UI updates from the thread must go through Invoke (Control.Invoke() in WinForms, and Dispatcher.Invoke() in WPF).
Second, when stopping data streaming or closing the application, the user needs to stop the background thread (as well as any activity on the UI Thread) before closing the application.
This logic could be accomplished with a few parameters and the asynchronous method:
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_stop = true; // stop background thread
if (_invokeInProgress)
{
e.Cancel = true; // cancel the initial closing
// wait until current invoke finishes
await Task.Factory.StartNew(() =>
{
while (_invokeInProgress) ;
});
// and then close the form
this.Close();
}
}
For example, on my reasonable modern computer, when the initial count is 2 x 1M points, adding new points (1000 per update) chart update just a little bit more often than once per second (~1.3 FPS). When the initial count is 2 x 10M points, new points could be added only once every 10 seconds at best. This shows how slow the .NET Chart update is even with the FastLine.
Once again, in comparison, LightningChart .NET could stream data 100 or thousands of times faster. For example, LightningChart can stream 2 x 1M points at 60 FPS. While LightningChart .NET with 2 x 100M points could be updated/refreshed with new points every half second.
Table 2. Chart update tests (1/1000th of points added to chart every cycle and scrolled).
Advantages of FastLine.NET Chart
- Free to use, if the .NET Framework is installed.
Disadvantages of FastLine.NET Chart
- Not a fast enough chart if it has millions of points.
- Unusable when the chart should render 10+ million points.
- Not mouse-interactive.
- It is only available on .NET Framework (not in .NET Core 3.x, .NET 5.0, .NET 6.0, or .NET 7.0).
- Mainly supported in Windows Forms, although in WPF could be used by adding WPF Toolkit (
WPFToolkit.dllandSystem.Windows.Controls.DataVisualization.Toolkit.dllassemblies should be added to the project).
LightningChart .NET Control for Data Visualization
The advantages of LightningChart .NET control are that is faster, more versatile, could be used in WinForms/WPF/UWP, and could be used in .NET Frameworks as well as in .NET (from Core 3.0 to 7.0) applications.
Once the LigthningChart (.NET) SDK is installed, it is possible to drop LightningChart control from Visual Studio Toolbox in the .NET Framework project (i.e. Form). However, to make any future library version update easier, we recommend creating a Chart in the code behind (*.cs file). For Winforms control, the user should call the constructor for control and set it to Parent and Dock properties.
_chart = new LightningChart();
_chart.Parent = panel2;
_chart.Dock = DockStyle.Fill;
The signal generation part of the code is like the previous example. While adding new Series and configuring axes range may be even simpler:
//Disable rendering.
_chart.BeginUpdate();
// create SampleDataBlockSeries
SampleDataBlockSeries sdbs1 = new SampleDataBlockSeries(_chart.ViewXY, _chart.ViewXY.XAxes[0], _chart.ViewXY.YAxes[0]);
_chart.ViewXY.SampleDataBlockSeries.Add(sdbs1); // add series to the chart
sdbs1.SamplingFrequency = 1 / _xStep;
sdbs1.FirstSampleTimeStamp = 0;
sdbs1.Color = DefaultColors.SeriesForBlackBackground[0];
sdbs1.AddSamples(yValues1, false); // add points/samples to Series
// create SampleDataBlockSeries
SampleDataBlockSeries sdbs2 = new SampleDataBlockSeries(_chart.ViewXY, _chart.ViewXY.XAxes[0], _chart.ViewXY.YAxes[0]);
_chart.ViewXY.SampleDataBlockSeries.Add(sdbs2); // add series to the chart
sdbs2.SamplingFrequency = 1 / _xStep;
sdbs2.FirstSampleTimeStamp = 0;
sdbs2.Color = Color.OrangeRed;
sdbs2.AddSamples(yValues2, false); // add points/samples to Series
// configure Axes
_chart.ViewXY.XAxes[0].SetRange(0, xMax);
_chart.ViewXY.YAxes[0].SetRange(-10, 12);
// some Chart styling
_chart.Title.Text = ($"LightningChart .NET ({iPointCount:N0} points per series)");
_chart.ViewXY.XAxes[0].Title.Text = "Horizontal Axis title";
_chart.ViewXY.YAxes[0].Title.Text = "Vertical Axis title";
//Allow rendering.
_chart.EndUpdate();
Series with data creation tests are presented in Table 1. The resulting figure/chart may look like this:
Generally, the Color-themes and the color of any item on the chart can be specifically configured very easily with one of (almost 2000) properties. However, this article doesn’t cover LightningChart .NET properties, for more information please check the API resources.
In this example, it is also possible to change the points count per series and test how fast the chart updates when all a point is replaced at once. As LightningChart is capable of visualizing far more points (without downsampling), users may experience some memory issues.
In particular, by default, the maximum size of an Array for a .NET application is 2 gigabytes and the number of elements within an array could not exceed 2×109. However, fortunately, in a 64-bit environment with 4.5 .NET, this limit could be removed with the gcAllowVeryLargeObjects configuration option. App.config file may look like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
</configuration>
Next, as in the above example, we would like to test LightningChart’s data-streaming capabilities. LightningChart doesn’t only have the scrolling mode but also several XAxisScrollMode.
If the user is interested, the user may test it by changing just one line in the code. The rest of the ThreadLoop method has very similar logic as in the example above.
private void ThreadLoop()
{
int iNewPointCount = iPointCount / 1000;
_chart.ViewXY.XAxes[0].ScrollMode = XAxisScrollMode.Scrolling;
while (_stop == false)
{
_invokeInProgress = true; // let the form know, if an invoke has started
_chart.Invoke(new Action(() => // update Chart in UI Thread
{
float[] yValues1 = new float[iNewPointCount];
float[] yValues2 = new float[iNewPointCount];
for (int iNew = 0; iNew < iNewPointCount; iNew++)
{
yValues1[iNew] = (float)(20 * Math.Abs(Math.Round((float)(iPointCount + iNew) / signalPeriod) - (float)(iPointCount + iNew) / signalPeriod));
yValues2[iNew] = (float)(8.0 * Math.Sin(2 * Math.PI * (float)(iPointCount + iNew) / signalPeriod));
}
// add points/samples to Series
_chart.ViewXY.SampleDataBlockSeries[0].AddSamples(yValues1, false);
_chart.ViewXY.SampleDataBlockSeries[1].AddSamples(yValues2, false);
_lastXValue = _lastXValue + iNewPointCount * _xStep;
_chart.ViewXY.XAxes[0].ScrollPosition = _lastXValue;
iPointCount = iPointCount + iNewPointCount;
_chart.Title.Text = ($"LightningChart .NET ({iPointCount:N0} points per series)");
}));
_invokeInProgress = false; // the invoke is complete
// Sleep a little bit so that UI stays responsive.
Thread.Sleep(1);
}
_thread = null;
}
Once again same rules are applied for LigthningChart when building multithreaded applications. That is thread-safe chart update and stopping & cleaning threads when closing the application. Scrolling test results are presented in Table 2.
Conclusion
In this article, we discussed 2 ‘fast line chart’ alternatives. One is a .NET Chart class with the FastLine type series. Another is the LightningChart .NET control. While we show that FastLine .NET Chart is a nice and easy-to-use free tool, unfortunately for ‘big data’ this is just too slow. Thankfully, LightningChart .NET control provides the best performance when measured by speed and quality of rendering.
Get started with LightningChart .NET for scientific data visualization
Swing index indicator: formula and implementation with LC JS Trader
Learn the Swing Index indicator formula and implementation with LightningChart JS Trader to detect trend direction and refine trading signals.
How to use the Supertrend indicator for Fintech app development
Learn about the Supertrend indicator in fintech app development to generate clear buy and sell signals, optimize ATR settings, and enhance trading strategies.
Using the Schaff Trend Cycle Indicator for Fintech App Development
Learn how the Schaff Trend Cycle combines MACD and stochastic logic to deliver faster, smoother momentum signals for fintech trading applications.
