

How do you display 100,000,000 data points in a WinForms line chart? The answer depends entirely on which charting library you choose — and on a detail almost every comparison gets backwards: on the right engine, WinForms is not slower than WPF. It is faster.
This post provides complete, working C# code for rendering 100 million points across the WinForms charting libraries developers actually evaluate — ProEssentials, LightningChart, DevExpress, Syncfusion, ComponentOne and ScottPlot. It shows what happens to your data at each step, and why the same ProEssentials engine renders roughly 5% faster on WinForms than on WPF.
If you've searched for "plot millions of points WinForms," "fastest WinForms chart," or "WinForms chart 100 million points," this is the concrete, code-level answer — with a clone-and-run GitHub repo so you can reproduce every number yourself.
Try it yourself: the WinForms 100M-point demo is a public repo — clone it, press F5, and watch 100 million points render on the hDC. No account, no trial signup, no gridded stand-in data.
Reproduce the WinForms numbers yourself: clone, build, run the WinForms 100M-point chart demo on GitHub.Compare against the WPF build: clone, build, run the WPF 100M-point chart demo on GitHub.
Before the code, the architectural point that reframes everything. A GPU chart on WPF cannot draw Direct3D straight to the screen — WPF's compositor owns the pixels, so the library must render to an off-screen texture, hand it to WPF via D3DImage, and let the compositor blend it in. Every frame pays a texture-copy and a compositor-sync cost.
A native WinForms control owns a real Win32 window handle and device context (hDC). ProEssentials couples Direct3D directly to that hDC — the compute-shader-constructed frame is presented straight to the window, with no render-to-texture and no second composite. Across representative datasets this makes the native WinForms path roughly 5% faster end-to-end than the identical engine driving a WPF control.
Same compute shader, same zero-copy data path, same on-demand model. WinForms is not the compromise interface — it is the fast one, because it skips WPF's compositor entirely.
Every library receives the same test: render a single-series line chart from 100 million sequential float values on a native WinForms form. We measure memory overhead beyond the source array, render time, and whether the library shows every point or a downsampled approximation.
| Parameter | Value |
|---|---|
| Data points | 100,000,000 (100 million) |
| Data type | float (4 bytes per value) — 400 MB raw data |
| Chart type | 2-D line chart, single series, sequential X-axis |
| Pattern | Sine wave with random noise (realistic signal data) |
| Platform | WinForms (.NET 8), Windows 11, mid-range GPU |
| What we measure | Lines of code, memory overhead, render time, data fidelity |
Below, we show the complete C# code for each library, annotated with what happens to your data at each step. Pay attention to one thing above all: whether the 100M values are copied, converted, or wrapped in objects — and whether the chart presents directly to the window or routes through an interop layer.
ProEssentials uses UseDataAtLocation() to point the WinForms chart directly at your existing float array. No copy, no conversion, no object wrapping. The Direct3D compute shader constructs all 100M vertices on the GPU and presents the frame straight to the control's hDC.
// ProEssentials — Plot 100 Million Points (WinForms, ~15 lines)
// NuGet: Install-Package ProEssentials.Chart.Net.Winforms
// 1. Allocate your data — this is the ONLY copy that will ever exist
float[] yData = new float[100_000_000];
for (int i = 0; i < yData.Length; i++)
yData[i] = (float)Math.Sin(i * 0.0001) + (float)(rand.NextDouble() * 0.1);
// 2. Configure the WinForms chart control (PesgoWin)
pesgo1.PeData.Subsets = 1;
pesgo1.PeData.Points = 100_000_000;
pesgo1.PePlot.Method = SGraphPlottingMethod.Line;
pesgo1.PeConfigure.RenderEngine = RenderEngine.Direct3D;
pesgo1.PeData.ComputeShader = true;
pesgo1.PeData.Filter2D3D = true; // GPU pre-filter shader — lossless min/max
// 3. Zero-copy: point the chart at your existing array — no duplication
pesgo1.PeData.Y.UseDataAtLocation(yData, yData.Length);
// 4. Tell the Direct3D engine to rebuild vertices and colors
pesgo1.PeFunction.Force3dxVerticeRebuild = true;
pesgo1.PeFunction.Force3dxNewColors = true;
// 5. Render — presented DIRECTLY to the WinForms window hDC
// (no render-to-texture, no WPF compositor hop)
pesgo1.PeFunction.ReinitializeResetImage();The key line is UseDataAtLocation(yData, yData.Length). This is not a copy operation — it's a pointer assignment. The 400 MB your data already occupies is the only copy that will ever exist; the chart adds essentially zero overhead.
The Force3dxVerticeRebuild and Force3dxNewColors flags tell the Direct3D engine to rebuild vertices and colors on the GPU. With ComputeShader = true and Filter2D3D = true, a GPU pre-filter computes lossless per-pixel-column min/max so no spike is lost — and the result is presented to the hDC without WPF's compositor hop.
~15 lines of C#. ~0 MB memory overhead. ~15 ms render time, ~5% faster than the same code on WPF. Lossless min/max filtering via GPU compute shader, presented directly to the WinForms hDC.
SciChart is frequently cited as the fastest WPF chart, but on WinForms there is a catch that no code sample can work around: SciChart has no native WinForms control. Its own documentation states WinForms is supported only through integration with WPF — by hosting the WPF SciChartSurface inside a Microsoft ElementHost.
// SciChart — Plot 100 Million Points (WinForms)
// NOTE: SciChart has NO native WinForms control.
// WinForms is supported only by hosting the WPF control in an ElementHost.
// 1. Host the WPF SciChartSurface inside a WinForms ElementHost
var host = new System.Windows.Forms.Integration.ElementHost();
host.Dock = DockStyle.Fill;
var sciChartSurface = new SciChart.Charting.Visuals.SciChartSurface();
host.Child = sciChartSurface; // WPF control living inside WinForms
this.Controls.Add(host);
// 2. From here the code is identical to the WPF path — and inherits
// WPF's render-to-texture compositor cost plus ElementHost interop
// limitations (mouse events, focus, z-order / "airspace").
var dataSeries = new XyDataSeries<double, double>(); // double[], not float[]
dataSeries.Append(xData, yData); // full internal copy
var lineSeries = new FastLineRenderableSeries {
DataSeries = dataSeries,
ResamplingMode = ResamplingMode.Auto // downsamples to ~viewport width
};
sciChartSurface.RenderableSeries.Add(lineSeries);
// There is no native WinForms SciChart control to target.That means a SciChart WinForms app inherits WPF's render-to-texture compositor path plus ElementHost's documented interop limitations around mouse events, focus, and z-ordering. The 100M-point code is effectively the WPF code, wrapped — and it carries the compositor cost that native WinForms avoids.
For a true native WinForms application, there is no first-class SciChart path. The category's headline performance name does not field a native WinForms entry, which is why it is shown here as interop rather than a code sample.
LightningChart is the strongest genuinely-native WinForms rival. It renders 100 million points through its DirectX pipeline using a specialized sample series, copying your float[] into its internal storage via an AddSamples-style call.
// LightningChart — Plot 100 Million Points (WinForms)
// NuGet: Install-Package LightningChart.NET (native WinForms control)
// 1. Allocate your data
float[] yData = new float[100_000_000];
for (int i = 0; i < yData.Length; i++)
yData[i] = (float)Math.Sin(i * 0.0001) + (float)(rand.NextDouble() * 0.1);
// 2. Configure the native WinForms chart for large data
lightningChart.BeginUpdate();
lightningChart.ViewXY.XAxes[0].SetRange(0, 100_000_000);
// 3. SampleDataSeries (fixed-interval) — copies float[] into internal buffer
var series = new SampleDataSeries(lightningChart.ViewXY,
lightningChart.ViewXY.XAxes[0], lightningChart.ViewXY.YAxes[0]);
series.FirstSampleTimeStamp = 0;
series.SamplingFrequency = 1;
// 4. Add all 100M samples — array copy (not zero-copy)
series.AddSamples(yData, false);
// 5. Add series; continuous DirectX render loop keeps the GPU active
lightningChart.ViewXY.SampleDataSeries.Add(series);
lightningChart.EndUpdate();It renders all 100M points losslessly (with the correct series type), which is a real strength. The tradeoffs match the broader comparison: a continuous DirectX render loop keeps the GPU active even when nothing changes, and the data path copies rather than referencing your array.
LightningChart is the closest native WinForms competitor on raw capability. The differences come down to the array copy, the continuous-loop power draw, and activation/licensing — not to whether it can render the points.
DevExpress is the interesting inversion: its fast large-data view, SwiftPlotSeriesView, is WinForms-only — it does not exist in the WPF ChartControl. So WinForms is actually DevExpress's stronger surface for large data.
// DevExpress — Plot 100 Million Points (WinForms)
// NuGet: DevExpress.Win.Charts
// NOTE: SwiftPlotSeriesView is WinForms-ONLY — it does not exist in WPF.
// 1. Create data model — DevExpress uses object-per-point
public class DataPoint {
public double Argument { get; set; }
public double Value { get; set; }
}
// 2. Allocate the objects (object-per-point is the binding constraint)
var data = new List<DataPoint>(100_000_000);
for (int i = 0; i < 100_000_000; i++)
data.Add(new DataPoint {
Argument = i,
Value = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1
});
// ⚠ Billions of bytes of DataPoint objects before rendering starts.
// DevExpress markets 20M+ "without preprocessing" — short of 100M.
// 3. SwiftPlotDiagram + SwiftPlotSeriesView (the WinForms fast path)
var series = new Series("Signal", ViewType.SwiftPlot);
series.DataSource = data;
series.ArgumentDataMember = "Argument";
series.ValueDataMembers.AddRange("Value");
chartControl.Series.Add(series);
((SwiftPlotDiagram)chartControl.Diagram).EnableAxisXScrolling = true;SwiftPlot uses a lightened generation algorithm and an object-per-point data model. At 100 million points you must allocate enormous numbers of SeriesPoint objects before rendering begins; in practice this is the binding constraint long before the renderer is. DevExpress markets 20M+ points without preprocessing — well short of 100M.
Syncfusion's headline "Fast Series" (FastLineSeries, FastLineBitmapSeries) exist only on WPF, WinUI, and UWP. On WinForms there is no fast-series path at all — Syncfusion's own support team logged the feature request and stated there was no immediate plan to implement it.
That leaves standard GDI+ rendering with no GPU acceleration for WinForms. At 100 million points this is not a practical scenario; Syncfusion WinForms is a strong choice for business dashboards under ~100K points, not for large-scale scientific acquisition.
ScottPlot is the dominant free/open-source WinForms option. It renders via System.Drawing then SkiaSharp on the CPU, and relies on data decimation — reducing your dataset to a representative subset of visual points — to stay responsive.
// ScottPlot — Plot 100 Million Points (WinForms, free / open-source)
// NuGet: Install-Package ScottPlot.WinForms
// 1. Allocate your data
double[] yData = new double[100_000_000];
for (int i = 0; i < yData.Length; i++)
yData[i] = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1;
// 2. SignalPlot is the large-data path (assumes even X spacing)
formsPlot1.Plot.Add.Signal(yData);
// 3. ScottPlot renders on the CPU (System.Drawing -> SkiaSharp) and
// relies on DECIMATION — it draws a representative subset, not all
// 100M points. Init for 10M+ runs 100+ ms; the displayed line is
// not lossless.
formsPlot1.Refresh();For 10M+ points, initialization runs 100+ ms and the displayed result is decimated, not lossless. ScottPlot's maintainer notes that the managed-to-native marshalling of point arrays is the fundamental ceiling. Excellent value for moderate data; not a lossless 100M engine.
Here's what happens when each library attempts to plot 100,000,000 float values on a native WinForms form:
| Factor | ProEssentials | SciChart | LightningChart | DevExpress | Syncfusion | ScottPlot |
|---|---|---|---|---|---|---|
| Native WinForms? | ✅ Yes | ❌ ElementHost | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Plots 100M? | ✅ Lossless | ⚠️ via WPF, resampled | ✅ Lossless | ⚠️ object-per-point | ❌ No fast path | ⚠️ Decimated |
| Data loading | Zero-copy pointer | Copy into double[] | Array copy | Object-per-point | GDI+ iteration | Array (managed↔native) |
| Library overhead | ~0 MB | ~800 MB | ~400 MB | ~2,400 MB+ | — | ~copy |
| Render time | ~15 ms (GPU) | via WPF | Fast | Fast (lightened) | Slow (GDI+) | 100+ ms init |
| Data fidelity | 100% lossless | Resampled | 100% | Feature-reduced | Lossless but slow | Decimated |
| GPU rendering | Compute shaders | (WPF) DirectX | DirectX | GDI+/DirectX | None | CPU |
| Present path | Direct to hDC | WPF compositor | Direct | Direct | Direct | Direct |
| vs WPF (same engine) | ~5% faster | — | — | — | — | — |
Two architectural facts compound on WinForms. First, when ProEssentials calls UseDataAtLocation(), it stores a pointer to your array rather than copying it — zero memory overhead. Second, the finished frame is presented to the hDC directly, with none of WPF's texture-copy-and-composite overhead.
The benefits cascade. Loading time is instant (a pointer assignment vs. iterating 100M values). Memory stays flat at your original 400 MB. And the present path is shorter than WPF's, which is where the ~5% end-to-end edge comes from — the same engine, minus the compositor.
Competitors that copy (LightningChart's AddSamples), convert (double[] storage), or wrap (object-per-point) multiply the per-value memory. At 100M points those multipliers are the difference between a flat 400 MB and gigabytes — or an OutOfMemoryException before the chart ever draws.
| Data Model | Overhead (100M pts) | Used By |
|---|---|---|
| Zero-copy pointer | ~0 MB | ProEssentials |
| Array copy (float) | ~400 MB | LightningChart |
| Array copy (double) | ~800 MB | SciChart (via WPF) |
| Object-per-point | ~2,400 MB+ | DevExpress |
| CPU decimation | copy + subset | ScottPlot |
100M floats × 4 bytes = 400 MB (your data). ProEssentials adds ~0 MB and presents to the hDC. The WPF version of the same engine adds a per-frame texture copy + compositor sync — which is exactly the ~5% the native WinForms path saves.
Telerik, ScottPlot, and DevExpress's lightened view all reduce data in some form — display a representative subset rather than every point — to stay responsive at scale. For trend overviews this is often visually fine.
But a decimated chart can hide a single-sample event: an ECG arrhythmia, a vibration resonance spike, a microsecond semiconductor excursion. If the subset doesn't happen to include that sample, you never see it. "Handles millions of points" by averaging them means rendering a summary of your data, not your data.
ProEssentials takes a different approach with its Filter2D3D GPU compute shader. Rather than dropping points, it computes the correct per-pixel-column min/max across all 100M values, so every spike survives — losslessly, on the hDC.
Of the native WinForms options, only ProEssentials and LightningChart realistically render 100M losslessly; DevExpress tops out lower with object-per-point, SciChart has no native control, Syncfusion has no fast path, and ScottPlot decimates. Here's the practical summary:
ProEssentials is the only WinForms library that combines zero-copy data loading, lossless GPU compute shader filtering, on-demand rendering, and direct hDC presentation — and it does so ~5% faster than the same engine on WPF, with a public clone-and-run repo to prove it.
Multiple libraries can attempt 100 million points in WinForms, but the landscape is narrower than WPF's. Only ProEssentials does it with zero memory overhead, lossless fidelity, and direct hDC presentation — and faster than its own WPF build.
SciChart has no native WinForms control. Syncfusion has no WinForms fast path. ScottPlot decimates. DevExpress's SwiftPlot is WinForms-only but object-per-point. LightningChart is the genuine native contender, with array-copy and continuous-loop tradeoffs.
If your WinForms application needs to display 100 million points — sensor data, signal processing, scientific acquisition, LiDAR — the hDC-coupled compute-shader path is the fastest lossless option available, and the only one you can clone and reproduce today.
The full hDC-coupling story, the 8-library WinForms comparison, and reproducible repos.
Read morepe_query.py validates every property path against the compiled DLL binary — no hallucinated API calls.
Read more5-year TCO across the major libraries. Perpetual vs. subscription licensing, free unlimited support.
Read moreAll competitor code examples and claims in this post were constructed from official documentation, public GitHub repositories, and vendor support statements. Verify them directly:
SciChart
LightningChart
DevExpress
Syncfusion
ComponentOne, Telerik & ScottPlot
ProEssentials is available on NuGet with no registration, no account, and no sales call. Install the WinForms package, clone the 100M-point repo, and reproduce every number on this page.
Contact the ProEssentials Team →Your success is our #1 goal by providing the easiest and most professional benefit to your organization and end-users.
ProEssentials was born from professional Electrical Engineers needing their own charting components. Join our large list of top engineering companies using ProEssentials.
Thank you for being a ProEssentials customer, and thank you for researching the ProEssentials charting engine.