C#でグラフ描画する方法|Chart・ScottPlot・OxyPlotの選び方と折れ線・リアルタイム表示の実装手順
はじめに
C#でグラフ描画を行う方法はいくつかあります。Windows Formsで手早く折れ線グラフを表示したい場合もあれば、WPFアプリでMVVMに沿ってデータを可視化したい場合、センサー値やログをリアルタイムに表示したい場合もあります。
代表的な選択肢としては、Windows FormsのChartコントロール、手軽で高速なScottPlot、WPFとの相性がよいOxyPlotがあります。ChartはMicrosoftのSystem.Windows.Forms.DataVisualization.Charting名前空間で提供されるWindows Forms向けのChartコントロールで、SeriesとChartAreasを中心にグラフを構成します。Microsoft Learn+1 ScottPlotは.NET向けの無料・オープンソースの描画ライブラリで、大量データをインタラクティブに表示しやすいことを特徴としています。
ScottPlot+1 OxyPlotは.NET向けのクロスプラットフォームなプロットライブラリで、WPFでは
PlotViewにPlotModelをバインドして使う構成が基本です。nuget+1
この記事では、C#でグラフ描画を始めるときに迷いやすいライブラリ選定から、折れ線グラフ、複数系列、リアルタイム表示、エラー対処までを実装例付きで解説します。
1. C#でグラフ描画する方法の全体像
1-1. C#でグラフ描画が必要になる主なケース
C#でグラフ描画が必要になる場面は、業務アプリ、計測アプリ、ログ監視ツール、データ分析ツール、管理画面など幅広くあります。たとえば、売上推移を折れ線グラフで表示する、温度や電圧などのセンサー値をリアルタイムに可視化する、CSVファイルの数値を棒グラフにする、といった用途です。
C#アプリでは、単に数値を表で表示するだけでは変化や傾向を把握しにくいことがあります。グラフ描画を取り入れることで、異常値、増減傾向、周期性、複数データの比較が分かりやすくなります。
1-2. Windows Forms・WPF・Blazor・コンソールアプリでの違い
C#でグラフ描画を行う場合、まずアプリの種類によって選ぶ方法が変わります。
Windows Formsでは、標準的な画面部品としてChartコントロールを使いやすく、簡単な折れ線グラフや棒グラフなら短いコードで実装できます。ScottPlotもWindows Forms向けのScottPlot.WinFormsパッケージが用意されており、FormsPlotを画面に配置して使えます。ScottPlot
WPFでは、XAMLとデータバインディングを活用できるため、OxyPlotやScottPlot.WPFが候補になります。ScottPlot.WPFではScottPlot.WPFパッケージをインストールし、XAMLに名前空間を追加してWpfPlotを配置する流れが公式のクイックスタートで示されています。ScottPlot
Blazorでは、Webアプリ上でグラフを表示する必要があるため、JavaScript系グラフライブラリとの連携やBlazor対応の描画ライブラリを検討します。コンソールアプリでは、画像ファイルとしてグラフを出力したり、ScottPlotなどで静的なプロット画像を生成したりする方法があります。
1-3. まず決めるべき「グラフの種類」「更新頻度」「配布環境」
ライブラリを選ぶ前に、次の3点を決めると失敗しにくくなります。
1つ目はグラフの種類です。折れ線グラフ、棒グラフ、散布図、ヒートマップ、円グラフなど、必要なグラフ形式によって向いているライブラリが変わります。
2つ目は更新頻度です。ボタンを押したときだけ更新するのか、1秒ごとに更新するのか、数十ミリ秒単位でリアルタイム波形を描画するのかで、求められる描画性能が大きく変わります。
3つ目は配布環境です。.NET FrameworkのWindows Formsアプリなのか、.NET 8や.NET 9のWPFアプリなのか、社内PCだけで動かすのか、商用アプリとして配布するのかを確認しておきましょう。
1-4. この記事で扱うChart・ScottPlot・OxyPlotの位置づけ
この記事では、C#でグラフ描画を行う際によく候補になる3つを扱います。
Chartは、Windows Formsで標準的な方法として使いやすいグラフ描画機能です。Visual Studioのフォームデザイナーから配置しやすく、簡単な業務アプリに向いています。
ScottPlotは、少ないコードで折れ線グラフや散布図を描きやすく、リアルタイム表示や大量データ表示にも使いやすいライブラリです。NuGetではScottPlot本体に加え、Windows Forms向けやWPF向けのパッケージが提供されています。nuget+1
OxyPlotは、PlotModelを中心にグラフを構成するライブラリで、WPFのMVVM構成と組み合わせやすいのが特徴です。OxyPlot.WpfはWPFアプリケーション向けのパッケージとして提供されています。nuget+1
2. C#のグラフ描画ライブラリの選び方
2-1. 標準機能で始めたいならChart
Windows Formsで簡単にグラフを表示したいなら、まずChartを検討するとよいでしょう。ChartはChartArea、Series、Legend、Titleなどの要素を組み合わせてグラフを作成します。Seriesはデータ系列を表し、DataPointを保持します。Microsoft Learn
Chartのメリットは、Windows Formsアプリに組み込みやすいことです。フォームにChartコントロールを配置し、コードからデータを追加すれば折れ線グラフや棒グラフを表示できます。
一方で、WPFで本格的に使いたい場合や、大量データを高速に描画したい場合、最新のUI構成に合わせたい場合は、ScottPlotやOxyPlotの方が扱いやすいケースがあります。
2-2. 手軽に高性能な折れ線グラフを描きたいならScottPlot
ScottPlotは、折れ線グラフ、散布図、棒グラフなどを短いコードで描けるライブラリです。公式サイトでも、ラインプロット、棒グラフ、円グラフ、散布図などを数行のコードで作成できることが説明されています。ScottPlot
特に、計測データ、ログ、時系列データ、リアルタイム波形などを表示したい場合に向いています。Windows FormsではScottPlot.WinForms、WPFではScottPlot.WPFを使うことで、それぞれのUIにグラフを組み込めます。
初心者にとっても、データ配列を用意してAdd.Scatter()などで描画できるため、C#でグラフ描画を学び始める際の選択肢として扱いやすいです。
2-3. WPFやMVVM構成で柔軟に使いたいならOxyPlot
WPFアプリで、MVVMパターンを意識してグラフ描画したい場合はOxyPlotが候補になります。OxyPlotでは、PlotModelを作成し、その中に軸や系列を追加し、WPF側のPlotViewにバインドします。NuGetのOxyPlot.Wpf説明でも、PlotViewをUIに追加し、PlotModelを作成してPlotViewのModelプロパティにバインドする流れが示されています。nuget+1
ビューとロジックを分けたい場合、ViewModel側にPlotModelを持たせて更新する構成にできます。WPFで保守性の高いアプリを作るなら、ChartよりOxyPlotの方が設計に合わせやすいことがあります。
2-4. 商用利用・ライセンス・メンテナンス性で比較する
商用アプリにグラフ描画ライブラリを組み込む場合は、ライセンス確認が重要です。ScottPlotはGitHub上でMITライセンスとして提供され、自由に変更・利用できることが説明されています。GitHub OxyPlot.WpfもNuGet上でMIT licenseと表示されています。
nuget
Chartについては、.NET FrameworkやWindows Forms環境で利用する場合の標準機能として使いやすい一方、古い.NET Framework向けChart Controlsの再頒布ではライセンス条項の確認が必要になるケースがあります。Microsoft
ライブラリを選ぶときは、ライセンスだけでなく、更新状況、ドキュメント、サンプルコード、NuGetパッケージの対応フレームワーク、将来的なメンテナンス性も確認しましょう。
2-5. リアルタイム表示に向いているライブラリを選ぶポイント
リアルタイム表示では、次のポイントが重要です。
更新間隔が短い場合、毎回すべてのデータ点を作り直すと描画が重くなります。古いデータを削除しながら一定件数だけ保持する、描画更新の頻度を下げる、UIスレッドで重い処理をしない、といった工夫が必要です。
リアルタイム波形や大量データを扱うなら、ScottPlotが扱いやすい選択肢です。業務画面で数秒ごとに数値を更新する程度ならChartでも十分です。WPFでMVVM構成を維持しながら定期更新したいならOxyPlotも選択肢になります。
3. Chart・ScottPlot・OxyPlotの比較
3-1. Chartの特徴・メリット・デメリット
Chartのメリットは、Windows Formsに組み込みやすく、基本的なグラフを作りやすいことです。折れ線グラフ、棒グラフ、円グラフ、散布図などを扱え、SeriesChartTypeを変更することで表示形式を切り替えられます。
デメリットは、主にWindows Forms向けであり、WPFやクロスプラットフォーム構成では使いにくい点です。また、大量データや高頻度更新では描画が重くなりやすいため、リアルタイム波形のような用途では注意が必要です。
3-2. ScottPlotの特徴・メリット・デメリット
ScottPlotのメリットは、少ないコードで見やすいグラフを作れること、Windows FormsやWPFで使いやすいこと、大量データの表示を意識していることです。ScottPlot.WinFormsのNuGet説明でも、大量データをインタラクティブに表示しやすい無料・オープンソースの.NET向けライブラリとされています。nuget
デメリットは、バージョンによってAPIが変わることがある点です。インターネット上の古いサンプルコードをそのまま使うと、ScottPlot 4系と5系の違いで動かない場合があります。導入時は公式ドキュメントとNuGetのバージョンを確認しましょう。
3-3. OxyPlotの特徴・メリット・デメリット
OxyPlotのメリットは、PlotModelを中心にグラフ構造を明確に作れることです。WPFアプリではViewModelにPlotModelを持たせやすく、MVVM構成と相性がよいです。
デメリットは、ChartやScottPlotに比べると、最初に理解する概念がやや多いことです。PlotModel、Axis、Series、DataPoint、InvalidatePlot()などを理解する必要があります。ただし、構成を理解すれば保守しやすいグラフ描画ができます。
3-4. 折れ線・棒グラフ・散布図・複数系列への対応比較
折れ線グラフは、Chart、ScottPlot、OxyPlotのいずれでも実装できます。棒グラフや散布図も対応可能です。複数系列についても、ChartならSeriesを複数追加し、ScottPlotなら複数のプロットを追加し、OxyPlotならLineSeriesなどを複数追加します。
基本的な業務グラフならChartで十分です。見た目や操作性、描画性能を重視するならScottPlotが便利です。WPFで軸や系列を細かく制御したいならOxyPlotが向いています。
3-5. 処理速度・描画性能・大量データ対応の比較
大量データでは、グラフライブラリの性能だけでなく、データの渡し方や更新方法が重要です。たとえば、10万点のデータを毎秒何度も全再描画すると、どのライブラリでも重くなります。
Chartは少量から中量の業務データ向けと考えると扱いやすいです。ScottPlotは大量データを表示しやすい設計のため、時系列データや計測値の可視化で使いやすいです。OxyPlotは柔軟ですが、高頻度更新では更新間隔やデータ点数を調整する必要があります。
3-6. 初心者が選ぶならどれが最適か
初心者がWindows Formsで簡単に始めたいならChartがおすすめです。フォームに配置して、Seriesに値を追加するだけでグラフ描画の基本を学べます。
一方、今から新しくC#でグラフ描画を学ぶならScottPlotもおすすめです。コードが短く、Windows FormsとWPFの両方で使いやすく、折れ線グラフやリアルタイム表示にも応用しやすいからです。
WPFで本格的なアプリを作る予定があり、MVVMを重視するならOxyPlotを選ぶとよいでしょう。
4. C#でChartを使ってグラフ描画する手順
4-1. Chartコントロールを使う準備
Windows FormsでChartを使うには、System.Windows.Forms.DataVisualization.Chartingを利用します。フォームデザイナーからChartコントロールを配置するか、コードでChartインスタンスを作成します。
C#using System.Windows.Forms.DataVisualization.Charting;
Chartでは、最低限次の要素を設定します。
C#var chartArea = new ChartArea("MainArea");
chart1.ChartAreas.Add(chartArea);
var series = new Series("Data");
series.ChartType = SeriesChartType.Line;
chart1.Series.Add(series);
ChartAreaは軸や描画領域を管理し、Seriesは実際のデータ系列を管理します。
4-2. Windows Formsで折れ線グラフを描画する
Windows Formsで折れ線グラフを描く基本コードは次のとおりです。
C#using System;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
chart1.Series.Clear();
chart1.ChartAreas.Clear();
chart1.ChartAreas.Add(new ChartArea("Area1"));
var series = new Series("売上");
series.ChartType = SeriesChartType.Line;
series.BorderWidth = 2;
series.Points.AddXY("1月", 120);
series.Points.AddXY("2月", 150);
series.Points.AddXY("3月", 130);
series.Points.AddXY("4月", 180);
chart1.Series.Add(series);
}
}
AddXY()を使うと、X軸とY軸の値をセットできます。X軸を月、Y軸を売上にすれば、月別売上の折れ線グラフになります。
4-3. X軸・Y軸・タイトル・凡例を設定する
Chartでは、タイトル、軸ラベル、凡例もコードで設定できます。
C#chart1.Titles.Clear();
chart1.Titles.Add("月別売上推移");
var area = chart1.ChartAreas["Area1"];
area.AxisX.Title = "月";
area.AxisY.Title = "売上";
area.AxisY.Minimum = 0;
chart1.Legends.Clear();
chart1.Legends.Add(new Legend("Legend1"));
軸の範囲を固定したい場合は、MinimumやMaximumを指定します。自動調整にしたい場合は、明示的な指定をしないか、必要に応じて再計算します。
4-4. 複数データ系列を追加する
複数系列を表示したい場合は、Seriesを複数作成してChartに追加します。
C#var seriesA = new Series("商品A")
{
ChartType = SeriesChartType.Line,
BorderWidth = 2
};
seriesA.Points.AddXY("1月", 100);
seriesA.Points.AddXY("2月", 120);
seriesA.Points.AddXY("3月", 140);
var seriesB = new Series("商品B")
{
ChartType = SeriesChartType.Line,
BorderWidth = 2
};
seriesB.Points.AddXY("1月", 80);
seriesB.Points.AddXY("2月", 110);
seriesB.Points.AddXY("3月", 160);
chart1.Series.Add(seriesA);
chart1.Series.Add(seriesB);
複数系列を使うと、商品別、拠点別、センサー別などの比較がしやすくなります。
4-5. Chartでリアルタイム更新する方法
Chartでリアルタイム更新する場合は、Timerを使って一定間隔でデータを追加します。
C#private int x = 0;
private Random random = new Random();
private void timer1_Tick(object sender, EventArgs e)
{
var series = chart1.Series["Data"];
series.Points.AddXY(x, random.Next(0, 100));
x++;
if (series.Points.Count > 100)
{
series.Points.RemoveAt(0);
}
chart1.ChartAreas[0].RecalculateAxesScale();
}
ポイントは、データ点を無限に増やさないことです。表示する点数を100件や1000件などに制限し、古いデータを削除すると描画が重くなりにくくなります。
4-6. Chartを使うときの注意点とつまずきやすいポイント
Chartでよくある失敗は、ChartAreaを作成していない、Series.ChartTypeを設定していない、データを追加しているのに画面を更新していない、軸の範囲が固定されていてデータが表示範囲外になっている、といったものです。
また、リアルタイム更新で重くなる場合は、データ点数を減らす、更新間隔を長くする、UIスレッドで重い計算をしない、まとめて描画するなどの対策が必要です。
5. C#でScottPlotを使ってグラフ描画する手順
5-1. ScottPlotをNuGetでインストールする
ScottPlotを使うには、NuGetからパッケージをインストールします。Windows FormsならScottPlot.WinForms、WPFならScottPlot.WPFを使います。ScottPlot.WinFormsの公式クイックスタートでは、ScottPlot.WinFormsをインストールし、FormsPlotをフォームに配置してデータを描画する流れが示されています。ScottPlot
Visual Studioの「NuGetパッケージの管理」から検索してインストールするか、パッケージマネージャーコンソールで次のように実行します。
PowerShellInstall-Package ScottPlot.WinForms
WPFの場合は次のようにインストールします。
PowerShellInstall-Package ScottPlot.WPF
5-2. Windows FormsでScottPlotを表示する
Windows Formsでは、フォームにFormsPlotを配置して使います。ツールボックスに表示されない場合は、公式クイックスタートでも案内されているように、プログラムから追加する方法もあります。ScottPlot
C#using ScottPlot.WinForms;
public partial class Form1 : Form
{
private FormsPlot formsPlot1 = new FormsPlot();
public Form1()
{
InitializeComponent();
formsPlot1.Dock = DockStyle.Fill;
Controls.Add(formsPlot1);
double[] xs = { 1, 2, 3, 4, 5 };
double[] ys = { 10, 20, 15, 30, 25 };
formsPlot1.Plot.Add.Scatter(xs, ys);
formsPlot1.Plot.Title("ScottPlot 折れ線グラフ");
formsPlot1.Plot.XLabel("X");
formsPlot1.Plot.YLabel("Y");
formsPlot1.Refresh();
}
}
5-3. WPFでScottPlotを表示する
WPFでは、ScottPlot.WPFをインストールし、XAMLに名前空間を追加してWpfPlotを配置します。公式クイックスタートでも、XAMLにScottPlot.WPFの名前空間を追加し、WpfPlotをレイアウトに配置する手順が示されています。ScottPlot
XML<Window x:Class="GraphSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF"
Title="GraphSample" Height="450" Width="800">
<Grid>
<ScottPlot:WpfPlot x:Name="WpfPlot1" />
</Grid>
</Window>
コードビハインドでは次のように描画します。
C#public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
double[] xs = { 1, 2, 3, 4, 5 };
double[] ys = { 2, 4, 3, 8, 6 };
WpfPlot1.Plot.Add.Scatter(xs, ys);
WpfPlot1.Plot.Title("WPF ScottPlot");
WpfPlot1.Refresh();
}
}
5-4. 配列データから折れ線グラフを描画する
ScottPlotでは、X座標とY座標をdouble[]で用意すると簡単に折れ線グラフを描画できます。
C#double[] xs = Enumerable.Range(0, 100).Select(i => (double)i).ToArray();
double[] ys = xs.Select(x => Math.Sin(x * 0.1)).ToArray();
formsPlot1.Plot.Add.Scatter(xs, ys);
formsPlot1.Refresh();
時系列データを扱う場合は、X軸をインデックスにする方法と、日時を数値に変換して扱う方法があります。まずはインデックスで描画し、必要になったら日時表示に対応するとよいでしょう。
5-5. 散布図・棒グラフ・複数系列を描画する
ScottPlotでは、複数の系列を同じグラフに追加できます。
C#double[] xs = { 1, 2, 3, 4, 5 };
double[] ys1 = { 10, 20, 15, 30, 25 };
double[] ys2 = { 8, 18, 12, 28, 20 };
formsPlot1.Plot.Add.Scatter(xs, ys1);
formsPlot1.Plot.Add.Scatter(xs, ys2);
formsPlot1.Plot.ShowLegend();
formsPlot1.Refresh();
棒グラフを描く場合も、ScottPlotのAPIを使ってデータを追加します。バージョンによってメソッド名や使い方が変わることがあるため、導入しているScottPlotのバージョンに合わせて公式ドキュメントを確認しましょう。
5-6. ScottPlotでリアルタイムグラフを更新する
リアルタイム表示では、一定件数の配列を更新しながら再描画します。
C#private readonly double[] ys = new double[200];
private readonly Random random = new Random();
private void timer1_Tick(object sender, EventArgs e)
{
Array.Copy(ys, 1, ys, 0, ys.Length - 1);
ys[ys.Length - 1] = random.NextDouble() * 100;
formsPlot1.Plot.Clear();
formsPlot1.Plot.Add.Signal(ys);
formsPlot1.Refresh();
}
この例では、古い値を左にずらして最新値を末尾に追加しています。点数を固定しているため、長時間動かしてもデータ数が増え続けません。
5-7. 大量データを高速に描画するコツ
大量データを扱う場合は、すべてのデータを毎回描画しないことが重要です。画面上で見える点数には限りがあるため、必要に応じて間引き、表示範囲の制限、更新頻度の調整を行います。
リアルタイム表示では、データ取得の頻度と描画の頻度を分けると安定しやすくなります。たとえば、センサー値は10ミリ秒ごとに取得しても、画面描画は100ミリ秒ごとにする、といった設計です。
6. C#でOxyPlotを使ってグラフ描画する手順
6-1. OxyPlotをNuGetでインストールする
WPFでOxyPlotを使う場合は、NuGetからOxyPlot.Wpfをインストールします。OxyPlot.Wpfの説明では、NuGetパッケージを追加し、PlotViewをUIに配置し、PlotModelを作成してModelプロパティにバインドする流れが示されています。nuget
PowerShellInstall-Package OxyPlot.Wpf
Windows Formsで使う場合はOxyPlot.WindowsFormsも用意されています。nuget
6-2. WPFでOxyPlotを表示する
WPFでOxyPlotを使うには、XAMLにPlotViewを配置します。
XML<Window x:Class="OxyPlotSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
Title="OxyPlotSample" Height="450" Width="800">
<Grid>
<oxy:PlotView Model="{Binding MyPlotModel}" />
</Grid>
</Window>
コードビハインドまたはViewModelでMyPlotModelを作成し、データコンテキストに設定します。
6-3. PlotModelを使って折れ線グラフを描画する
OxyPlotで折れ線グラフを作る基本コードは次のとおりです。
C#using OxyPlot;
using OxyPlot.Series;
public PlotModel MyPlotModel { get; set; }
public MainWindow()
{
InitializeComponent();
var model = new PlotModel { Title = "OxyPlot 折れ線グラフ" };
var series = new LineSeries
{
Title = "データ"
};
series.Points.Add(new DataPoint(1, 10));
series.Points.Add(new DataPoint(2, 20));
series.Points.Add(new DataPoint(3, 15));
series.Points.Add(new DataPoint(4, 30));
model.Series.Add(series);
MyPlotModel = model;
DataContext = this;
}
OxyPlotでは、LineSeriesにDataPointを追加して線を描きます。
6-4. X軸・Y軸・ラベル・凡例を設定する
軸を明示的に設定する場合は、LinearAxisを追加します。
C#using OxyPlot.Axes;
var model = new PlotModel
{
Title = "売上推移"
};
model.Axes.Add(new LinearAxis
{
Position = AxisPosition.Bottom,
Title = "月"
});
model.Axes.Add(new LinearAxis
{
Position = AxisPosition.Left,
Title = "売上",
Minimum = 0
});
凡例やラベルの設定もPlotModelや各Seriesで行います。複数系列を追加するときは、各LineSeriesにTitleを設定しておくと判別しやすくなります。
6-5. MVVMパターンでグラフを更新する
MVVMで使う場合は、ViewModelにPlotModelプロパティを用意します。
C#public class MainViewModel
{
public PlotModel MyPlotModel { get; }
public MainViewModel()
{
MyPlotModel = new PlotModel { Title = "MVVM Graph" };
var series = new LineSeries { Title = "値" };
series.Points.Add(new DataPoint(0, 10));
series.Points.Add(new DataPoint(1, 20));
MyPlotModel.Series.Add(series);
}
}
データを更新した後は、次のように再描画を通知します。
C#MyPlotModel.InvalidatePlot(true);
ViewModel側でデータを管理できるため、画面と処理を分離しやすくなります。
6-6. OxyPlotでリアルタイム表示する方法
OxyPlotでリアルタイム表示をする場合も、一定間隔でLineSeriesの点を更新し、InvalidatePlot(true)を呼び出します。
C#private LineSeries _series;
private int _x = 0;
private Random _random = new Random();
private void UpdateGraph()
{
_series.Points.Add(new DataPoint(_x, _random.Next(0, 100)));
_x++;
if (_series.Points.Count > 100)
{
_series.Points.RemoveAt(0);
}
MyPlotModel.InvalidatePlot(true);
}
WPFでは、UIに関係する更新はUIスレッドで行う必要があります。バックグラウンドでデータ取得を行う場合は、Dispatcherを使ってUIスレッドに戻してからグラフを更新します。
6-7. OxyPlotを使うときの注意点
OxyPlotでは、データを追加しただけでは画面が更新されないことがあります。その場合はInvalidatePlot(true)を呼び出します。
また、MVVMでPlotModelを差し替える場合は、INotifyPropertyChangedで変更通知を行う必要があります。既存のPlotModel内の系列だけを更新するのか、PlotModel全体を作り直すのかを決めておくと実装が安定します。
7. 折れ線グラフをC#で描画する実装例
7-1. 折れ線グラフに必要なデータ構造
折れ線グラフでは、基本的にX軸の値とY軸の値をペアで持ちます。C#では次のような配列やリストを使うことが多いです。
C#double[] xs = { 1, 2, 3, 4, 5 };
double[] ys = { 10, 20, 15, 30, 25 };
または、独自クラスを使ってデータを管理します。
C#public class GraphPoint
{
public double X { get; set; }
public double Y { get; set; }
}
CSVやデータベースから取得した値も、最終的にはXとYの組に変換してグラフライブラリへ渡します。
7-2. Chartで折れ線グラフを描くコード例
C#chart1.Series.Clear();
chart1.ChartAreas.Clear();
chart1.ChartAreas.Add(new ChartArea("Area1"));
var series = new Series("気温")
{
ChartType = SeriesChartType.Line,
BorderWidth = 2
};
double[] xs = { 0, 1, 2, 3, 4, 5 };
double[] ys = { 22.1, 22.5, 23.0, 23.4, 23.1, 22.8 };
for (int i = 0; i < xs.Length; i++)
{
series.Points.AddXY(xs[i], ys[i]);
}
chart1.Series.Add(series);
chart1.ChartAreas["Area1"].AxisX.Title = "時間";
chart1.ChartAreas["Area1"].AxisY.Title = "気温";
ChartではSeries.Points.AddXY()を使って点を追加します。
7-3. ScottPlotで折れ線グラフを描くコード例
C#double[] xs = { 0, 1, 2, 3, 4, 5 };
double[] ys = { 22.1, 22.5, 23.0, 23.4, 23.1, 22.8 };
formsPlot1.Plot.Clear();
formsPlot1.Plot.Add.Scatter(xs, ys);
formsPlot1.Plot.Title("気温の推移");
formsPlot1.Plot.XLabel("時間");
formsPlot1.Plot.YLabel("気温");
formsPlot1.Refresh();
ScottPlotでは配列をそのまま渡せるため、折れ線グラフをシンプルに実装できます。
7-4. OxyPlotで折れ線グラフを描くコード例
C#var model = new PlotModel { Title = "気温の推移" };
var series = new LineSeries
{
Title = "気温"
};
double[] xs = { 0, 1, 2, 3, 4, 5 };
double[] ys = { 22.1, 22.5, 23.0, 23.4, 23.1, 22.8 };
for (int i = 0; i < xs.Length; i++)
{
series.Points.Add(new DataPoint(xs[i], ys[i]));
}
model.Series.Add(series);
MyPlotModel = model;
OxyPlotではLineSeriesとDataPointを使って折れ線グラフを構成します。
7-5. 複数系列の折れ線グラフを描画する方法
複数系列を描画する場合は、系列ごとにデータを分けます。
C#double[] xs = { 1, 2, 3, 4, 5 };
double[] temperature = { 22, 23, 24, 23, 22 };
double[] humidity = { 50, 55, 53, 58, 60 };
ScottPlotなら次のように追加できます。
C#formsPlot1.Plot.Add.Scatter(xs, temperature);
formsPlot1.Plot.Add.Scatter(xs, humidity);
formsPlot1.Plot.ShowLegend();
formsPlot1.Refresh();
ChartならSeriesを2つ、OxyPlotならLineSeriesを2つ作成します。系列名を明確にして凡例を表示すると、利用者が読み取りやすくなります。
7-6. 軸の範囲を自動調整・固定する方法
軸の範囲は、自動調整にするか固定するかを用途に応じて選びます。
売上推移や月次レポートでは自動調整でも問題ありません。一方、リアルタイム波形やセンサー監視では、Y軸が毎回変わると変化が分かりにくくなるため、範囲を固定した方が見やすいことがあります。
ChartではAxisY.MinimumやAxisY.Maximumを設定します。OxyPlotでもLinearAxisのMinimumやMaximumを指定できます。ScottPlotでも軸範囲を指定するAPIを使って表示範囲を制御します。
8. リアルタイムグラフをC#で表示する方法
8-1. リアルタイム描画の基本的な考え方
リアルタイムグラフでは、データ取得、データ保持、描画更新を分けて考えることが大切です。
センサー値やログは高頻度で発生することがありますが、そのたびに画面を再描画すると重くなります。データは内部バッファに蓄積し、画面は一定間隔で更新する設計にすると安定します。
たとえば、データ取得は10ミリ秒ごと、画面更新は100ミリ秒ごとにするだけでも、UIの負荷を大きく下げられます。
8-2. Timerを使って一定間隔で更新する
Windows FormsではSystem.Windows.Forms.Timerを使って定期更新できます。
C#private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 100;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
UpdateGraph();
}
WPFではDispatcherTimerを使うことが多いです。
C#var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(100);
timer.Tick += (s, e) => UpdateGraph();
timer.Start();
UI更新を伴う処理は、UIスレッド上で実行する必要があります。
8-3. センサー値・ログ・CSVデータをリアルタイム表示する
センサー値を表示する場合は、最新値を受け取ったらキューやリングバッファに追加します。CSVをリアルタイム風に表示する場合は、一定間隔で1行ずつ読み込んでグラフに追加します。
C#Queue<double> values = new Queue<double>();
void AddValue(double value)
{
values.Enqueue(value);
while (values.Count > 200)
{
values.Dequeue();
}
}
保持する件数を制限することで、長時間動作してもメモリ使用量と描画負荷を抑えられます。
8-4. UIスレッドと非同期処理の注意点
C#のデスクトップアプリでは、UI部品を別スレッドから直接更新すると例外や不安定な動作の原因になります。データ取得をTask.Run()などで非同期実行する場合でも、Chart、FormsPlot、WpfPlot、PlotViewの更新はUIスレッドに戻して行います。
Windows FormsではInvoke()やBeginInvoke()、WPFではDispatcher.Invoke()やDispatcher.BeginInvoke()を使います。
C#BeginInvoke(new Action(() =>
{
UpdateGraph();
}));
重い計算やファイル読み込みはバックグラウンドで行い、画面更新だけをUIスレッドに渡す設計が基本です。
8-5. 描画が重くなる原因と改善策
リアルタイムグラフが重くなる主な原因は、データ点数が多すぎる、更新頻度が高すぎる、毎回グラフ全体を作り直している、UIスレッドでデータ処理も行っている、というものです。
改善策としては、表示点数を制限する、描画頻度を下げる、データを間引く、表示範囲外のデータを描画しない、データ取得と描画を分離する、などが有効です。
特に、計測アプリでは「取得した全データを保存すること」と「画面に全データを表示すること」を分けましょう。保存用には全件を保持し、表示用には直近の数百点だけを使う構成が実用的です。
8-6. リアルタイム表示におすすめのライブラリ
リアルタイム表示を重視するなら、ScottPlotが扱いやすい選択肢です。大量データのインタラクティブ表示を特徴としており、Windows FormsやWPFでも使いやすいパッケージが用意されています。nuget+1
簡単な業務アプリで数秒ごとに更新する程度ならChartでも問題ありません。WPFでMVVM構成を保ちながら更新したい場合はOxyPlotも候補になります。
9. C#グラフ描画でよく使うカスタマイズ
9-1. グラフタイトル・軸ラベル・凡例を設定する
グラフは、タイトル、軸ラベル、凡例を設定するだけで分かりやすさが大きく変わります。
Chartでは次のように設定します。
C#chart1.Titles.Add("売上推移");
chart1.ChartAreas[0].AxisX.Title = "月";
chart1.ChartAreas[0].AxisY.Title = "売上";
ScottPlotでは次のように設定します。
C#formsPlot1.Plot.Title("売上推移");
formsPlot1.Plot.XLabel("月");
formsPlot1.Plot.YLabel("売上");
formsPlot1.Plot.ShowLegend();
OxyPlotではPlotModel.TitleやAxis.Title、系列のTitleを使います。
9-2. 線の色・太さ・マーカーを変更する
線の色、太さ、マーカーを変更すると、複数系列のグラフが見やすくなります。
ChartではBorderWidthやMarkerStyleを設定します。
C#series.BorderWidth = 3;
series.MarkerStyle = MarkerStyle.Circle;
series.MarkerSize = 6;
OxyPlotではLineSeriesのStrokeThicknessやMarkerTypeを指定できます。
C#series.StrokeThickness = 2;
series.MarkerType = MarkerType.Circle;
ScottPlotでもプロット作成時や作成後にスタイルを設定できます。使用しているバージョンによってAPIが異なることがあるため、公式サンプルに合わせて設定しましょう。
9-3. グリッド線・背景色・余白を調整する
グリッド線は数値を読み取りやすくする一方で、多すぎると見づらくなります。業務アプリでは、薄いグリッド線を表示し、背景色はシンプルにするのがおすすめです。
余白が狭すぎると軸ラベルや凡例が切れることがあります。タイトルやラベルを追加した後は、実際の画面サイズで表示を確認しましょう。
9-4. 表示範囲をズーム・パンに対応させる
ユーザーがグラフを操作する場合、ズームやパンに対応していると便利です。ScottPlotはインタラクティブな表示を行いやすく、グラフの拡大縮小や移動を組み込みやすいライブラリです。ScottPlot
大量データを扱う場合は、最初から全体を表示するのではなく、特定範囲を表示し、必要に応じてズームやスクロールで確認できるようにすると使いやすくなります。
9-5. グラフ画像として保存する
レポート出力やメール添付に使う場合は、グラフを画像として保存できると便利です。Chart、ScottPlot、OxyPlotはいずれも画像出力に対応できます。
保存形式はPNGが扱いやすく、画面表示や資料貼り付けにも向いています。帳票や印刷用途では、画像サイズや解像度も意識しましょう。
9-6. CSVやデータベースの値をグラフ化する
CSVの値をグラフ化する場合は、まずファイルを読み込み、X軸とY軸の配列に変換します。
C#var lines = File.ReadAllLines("data.csv");
var xs = new List<double>();
var ys = new List<double>();
foreach (var line in lines.Skip(1))
{
var cols = line.Split(',');
xs.Add(double.Parse(cols[0]));
ys.Add(double.Parse(cols[1]));
}
データベースの場合も同様に、SQLで取得した結果をリストや配列に変換してからグラフライブラリに渡します。データ件数が多い場合は、SQL側で集計してから描画すると効率的です。
10. C#グラフ描画でよくあるエラーと対処法
10-1. グラフが表示されない原因
グラフが表示されない場合は、まずデータが入っているかを確認します。次に、グラフコントロールが画面に配置されているか、サイズが0になっていないか、SeriesやChartAreaが正しく追加されているかを確認します。
Chartでは、ChartAreaがない状態でSeriesだけ追加しても期待通りに表示されないことがあります。ScottPlotでは、データ追加後にRefresh()を呼び忘れていないか確認しましょう。OxyPlotでは、PlotModelがPlotViewにバインドされているかを確認します。
10-2. データを追加しても画面が更新されない原因
データを追加しても画面が変わらない場合は、再描画処理が不足している可能性があります。
ScottPlotではRefresh()、OxyPlotではInvalidatePlot(true)を呼び出します。Chartでは、データ追加後に自動で更新されることもありますが、軸範囲が固定されている場合は新しいデータが表示範囲外になっていることがあります。
10-3. リアルタイム更新で画面が固まる原因
リアルタイム更新で画面が固まる原因は、UIスレッドで重い処理をしていることが多いです。ファイル読み込み、通信、集計処理、データ変換をUIスレッドで行うと、画面操作が止まります。
対策として、データ取得や集計はバックグラウンドで行い、UIスレッドでは描画更新だけを行います。また、描画間隔を短くしすぎないことも重要です。
10-4. 軸のスケールが想定どおりにならない原因
軸のスケールが想定どおりにならない場合は、自動スケールと固定スケールの設定を確認します。最小値や最大値を固定していると、範囲外のデータが表示されません。
リアルタイムグラフでは、Y軸を自動にすると線が大きく揺れて見えることがあります。変化量を比較したい場合は固定スケール、常に全体を見たい場合は自動スケールを使うとよいでしょう。
10-5. NuGetインストール後に参照エラーが出る場合
NuGetでインストールした後に参照エラーが出る場合は、対象フレームワークとパッケージが合っているか確認します。Windows Forms用、WPF用、Core用など、パッケージが分かれているライブラリでは、アプリの種類に合ったパッケージを入れる必要があります。
ScottPlotならWindows FormsではScottPlot.WinForms、WPFではScottPlot.WPFを使います。OxyPlotならWPFではOxyPlot.Wpfを使います。ScottPlot+1
10-6. .NET Frameworkと.NETの違いによる注意点
古いWindows Formsアプリでは.NET Frameworkを使っていることがあります。一方、最近のC#アプリでは.NET 8や.NET 9などを使うことが増えています。
ライブラリによって対応しているフレームワークが異なるため、NuGetでインストールする前に、自分のプロジェクトのターゲットフレームワークを確認しましょう。インターネット上の古いサンプルは.NET Framework前提の場合があるため、そのまま最新の.NETプロジェクトに貼り付けると動かないことがあります。
11. 用途別おすすめライブラリ
11-1. 初心者が簡単にグラフ描画したい場合
初心者がC#でグラフ描画を学ぶなら、Windows FormsではChart、これから新しく学ぶならScottPlotがおすすめです。
Chartはフォームに配置してSeriesに値を追加する流れが分かりやすく、グラフ描画の基本を理解しやすいです。ScottPlotはコードが短く、折れ線グラフや散布図をすぐに描けるため、実用的なアプリにも発展させやすいです。
11-2. Windows Formsで手早く実装したい場合
Windows Formsで手早く実装したいなら、ChartまたはScottPlot.WinFormsが候補です。
既存の業務アプリに簡単なグラフを追加するならChartで十分です。見た目や操作性、リアルタイム表示、大量データへの対応を重視するならScottPlot.WinFormsを検討しましょう。
11-3. WPFアプリで本格的に実装したい場合
WPFアプリで本格的にグラフ描画するなら、OxyPlotまたはScottPlot.WPFがおすすめです。
MVVM構成を重視するならOxyPlotが向いています。PlotModelをViewModelで管理し、PlotViewにバインドする構成にできます。手軽さや描画性能を重視するならScottPlot.WPFも使いやすいです。
11-4. リアルタイム波形や計測データを表示したい場合
リアルタイム波形や計測データを表示したい場合は、ScottPlotが有力です。大量データのインタラクティブ表示を特徴としており、計測値や時系列データの可視化に使いやすいライブラリです。ScottPlot+1
ただし、ライブラリに任せるだけでなく、表示点数の制限、描画頻度の調整、UIスレッドの負荷軽減を行うことが重要です。
11-5. 大量データを高速に描画したい場合
大量データを高速に描画したい場合は、ScottPlotを優先的に検討するとよいでしょう。ScottPlotは.NET向けの無料・オープンソースライブラリで、大量データをインタラクティブに表示しやすいことを特徴としています。nuget+1
ただし、数百万点のデータを常に全点描画するのではなく、表示範囲に応じて間引きや集約を行う設計が必要です。
11-6. 商用アプリに組み込みたい場合
商用アプリに組み込む場合は、ライセンス、更新状況、サポート体制、依存関係を確認しましょう。ScottPlotはMITライセンスで提供されていることが明記されています。GitHub OxyPlot.WpfもNuGet上でMIT licenseと表示されています。
nuget
社内利用だけでなく外部配布する場合は、ライセンス表記の要否や配布条件を必ず確認してください。
12. C#のグラフ描画に関するよくある質問
12-1. C#だけでグラフ描画はできるのか
はい、C#だけでグラフ描画はできます。Windows FormsならChartコントロール、ScottPlot.WinForms、OxyPlot.WindowsFormsなどが使えます。WPFならScottPlot.WPFやOxyPlot.Wpfを使えます。
ただし、WebアプリやBlazorで高度なグラフを表示する場合は、JavaScript系ライブラリと連携する方法も選択肢になります。
12-2. 無料で使えるグラフ描画ライブラリはどれか
無料で使える代表的なライブラリとしてScottPlotとOxyPlotがあります。ScottPlotは無料・オープンソースの.NET向けプロットライブラリで、MITライセンスとして提供されています。ScottPlot+1 OxyPlot.WpfもNuGetでMIT licenseと表示されています。
nuget
商用利用する場合は、導入時点のライセンスを必ず確認しましょう。
12-3. ChartとScottPlotはどちらが使いやすいか
Windows Formsで簡単なグラフを作るだけならChartが分かりやすいです。フォームに配置し、Seriesにデータを追加するだけで基本的なグラフを作れます。
一方、今から新規に開発するならScottPlotの方が扱いやすい場面も多いです。配列から折れ線グラフを描きやすく、リアルタイム表示や大量データ表示にも応用しやすいからです。
12-4. WPFでおすすめのグラフ描画方法はどれか
WPFでは、OxyPlotまたはScottPlot.WPFがおすすめです。MVVM構成で保守しやすく作りたいならOxyPlot、短いコードで手軽に描画したいならScottPlot.WPFが向いています。
OxyPlotではPlotViewにPlotModelをバインドする構成が基本で、WPFのデータバインディングと組み合わせやすいです。nuget
12-5. リアルタイムグラフに最適なライブラリはどれか
リアルタイムグラフではScottPlotが使いやすい選択肢です。特に、折れ線グラフ、波形、時系列データ、ログ監視のように連続的に値を表示する用途に向いています。
ただし、最適なライブラリはデータ量と更新頻度によって変わります。数秒ごとの更新ならChartやOxyPlotでも十分な場合があります。数十ミリ秒単位で更新する場合は、描画頻度、表示点数、UIスレッド処理を慎重に設計しましょう。
12-6. PythonのmatplotlibのようにC#でグラフ描画できるか
はい、C#でもPythonのmatplotlibのようにグラフ描画できます。静的なグラフ画像を作るだけでなく、Windows FormsやWPFの画面上にインタラクティブなグラフを表示できます。
matplotlibに近い感覚で配列データから手早く描画したいならScottPlotが使いやすいです。WPFアプリとして設計を整えたいならOxyPlot、Windows Formsで標準的に始めたいならChartを選ぶとよいでしょう。
まとめ
C#でグラフ描画する方法は、アプリの種類、グラフの種類、更新頻度、配布環境によって選ぶべきライブラリが変わります。
Windows Formsで簡単に始めたいならChart、手軽に高性能な折れ線グラフやリアルタイム表示を実装したいならScottPlot、WPFやMVVM構成で柔軟に作りたいならOxyPlotがおすすめです。
折れ線グラフだけならどのライブラリでも実装できますが、リアルタイム表示や大量データ対応では、データ点数を制限する、描画頻度を調整する、UIスレッドをブロックしないといった設計が重要になります。
まずは小さなサンプルで折れ線グラフを描画し、その後に複数系列、軸設定、凡例、リアルタイム更新、CSVやデータベース連携へ広げていくと、C#でのグラフ描画を無理なく習得できます。

