【WPF】いちばんやさしいMSChart の使い方(WindowsForm共通)

当ページのリンクには広告が含まれています。

Visual Studio の WindowsForm では、マイクロソフト標準のMSChartコントロールが使えるのですが、WPFではサポートされておらずツールボックスからも消えています。

しかし、使えない事はありません。

WPFからWindowsFormのコンポーネントを呼び出す機能(WindowsFormsHost)が用意されていて、これを使う事でMSChartをWPFで利用できるようになります。

今回は、MSChartの使い方についてWPFを使って解説したいと思います。

尚、画面のレイアウトはXAMLを使っていますが、C#のコード部分はWindowsFormと共通なので、「WindowsFormでMSChartを使ってグラフを書きたい!」という方についても、参考にして頂ける内容となっています。

目次

MSChartの概要

MSChartには35種類のグラフ描画機能が用意されています。

実際には「カクカクした折れ線」と「滑らかな折れ線」などのように少しだけ違う似た者同士のグラフも多いので、厳密に言うともう少し減りますが、それでも、点、折れ線、縦/横棒、積み上げ、面、ピラミッド、ドーナツ、ローソク、レーダーチャート、練行足、範囲、株価など、様々な用途で使えるグラフが用意されています。

MSChartが他のフリーのチャートコントロールに勝る点は次の2つです。

  • 折れ線グラフの描画は高速で、Scott Plot に勝るとも劣らない
  • 3Dグラフに対応している

マイクロソフトの公式サイトも用意されていますが、ひたすらクラスの仕様(メソッド、プロパティ等)が記載されているだけで特にサンプルソースは見当たらず、これからグラフを書こうという方にはほとんど参考になりません。

ただ、MSChartの歴史が古いため、Google検索でヒットするブログ記事などには数多くのサンプルソースと解説が載っていますので、そちらは非常に参考になります。

ここがマイナスポイント

OxyPlot もそうでしたが、初期値のままだと見栄えが良くありません。

また、他のチャートコントロールに比べてグラフを描画するまでの処理が多いです。

デザインに関しては、以下のところがマイナスポイントです。

特に小さい領域でグラフと凡例を同時に表示することは避けた方が良さそうです。

インストール方法

MSChartはVisual StudioのWindowsFormに標準搭載されていますので、WindowsFormアプリケーションを作るのであれば、特に何もする必要はありません。

しかし、WPFでMSChartコントロールを使う場合は必ず参照設定が必要です。

VisualStudioのソリューションエクスプローラーから「参照」をクリックし、表示されるメニューから「参照の追加」を選択します。

すると、参照マネージャーというダイアログが表示されますので、左端の「アセンブリ」をクリックし、一覧の中から

System.Drawing と System.Windows.Forms.DataVisualization にチェックを入れてください。

使い方

WindowsFormの場合はツールボックスにコントロールが表示されるのでドラッグ&ドロップで張り付けられますが、WPFの場合はMSChartコントロール自身がツールボックスに表示されません。

代わりにWindowsFormsHost を画面にドラッグ&ドロップするか、XAMLに直接 WindowsFormsHostのタグを記述して、そのタグの中にMSChartを記述しなければなりません。

ドラッグ&ドロップしても結局XAMLを編集する必要があるので、最初からXAMLに記述する方が楽です。

XAMLへの記述は簡単で、次の様に書くだけです。

C#からMSChartにアクセスするためには名前が必要なので、ついでにx:Name で名前も付けておきましょう。

<WindowsFormsHost Height="315" Width="300" Margin="22,46,990,58" HorizontalAlignment="Left">
    <wfc:Chart x:Name="uxChart"/>
</WindowsFormsHost>

MSChartの構造

XAMLの参照が終われば、あとはWindowsFormで MSChartを使うのとまったく同じ手順になります。

描画手順は、MSChartコントロールに対して、タイトルや軸、プロットデータをセットすればグラフ表示されるのですが、他のフリーのチャートコントロールと異なる点は、Seriesクラスが1つしか用意されておらず、ChartType プロパティにグラフの種類をセットするという点です。

クラス構成は次の様になっています。

クラス構成を見てみると、Title、Legend、ChartArea、Seriesが全て並列に並んでいて、主従関係が無い構成になっています。

これに関しては、MSChart独特の考え方になっていますので、後ほど詳細を説明しますが、今はこのようなクラス構成であるという点だけ理解いただければと思います。

以下は、MSChartが対応しているグラフの一覧になります。

例えば、Seriesクラスの ChartType プロパティに ChartType.Point を代入すると、点グラフが表示され、ChartType.Lineを代入すると折れ線グラフが表示されるようになっています。

No種類ChartStyleNo種類ChartStyle
1ポイント グラフPoint19ピラミッド グラフPyramid
2FastPoint グラフFastPoint20ドーナツ グラフDoughnut
3バブル チャートBubble21株価チャートStock
4折れ線グラフLine22ローソクチャートCandlestick
5スプライン グラフSpline23範囲グラフRange
6StepLine グラフStepLine24スプライン範囲グラフSplineRange
7FastLine グラフFastLine25RangeBar グラフRangeBar
8横棒グラフBar26範囲縦棒グラフRangeColumn
9積み上げ横棒グラフStackedBar27レーダー チャートRadar
10100% 積み上げ横棒グラフStackedBar10028極座標チャートPolar
11縦棒グラフColumn29誤差範囲グラフErrorBar
12積み上げ縦棒グラフStackedColumn30ボックス プロット グラフBoxPlot
13100% 積み上げ縦棒グラフStackedColumn10031練行足チャートRenko
14面グラフArea32ThreeLineBreak グラフThreeLineBreak
15スプライン面グラフSplineArea33かぎ足チャートKagi
16積み上げ面グラフStackedArea34PointAndFigure グラフPointAndFigure
17100% 積み上げ面グラフStackedArea10035じょうごグラフFunnel
18円グラフPie 

MSChartの各クラスとグラフの関係

ChartコントロールのTitle、Legend、ChartArea、Seriesと描画されるグラフには、次のような関係があります。

Titleにはグラフのタイトルを保持するためのオブジェクトを登録します。

Legendは凡例のオブジェクトを登録しますが、1つの凡例オブジェクトで複数の凡例をひとまとまりに表示するため、「凡例表示エリア」と考えた方が分かりやすいと思います。

ChartAreaはグラフを表示するためのオブジェクトで、SeriesはChartAreaに表示される個々のグラフオブジェクトを登録します。

Title、Legend、ChartArea、SeriesはいずれもCollection クラスなのでオブジェクトはAddメソッドで登録していきます。

Title、Legend、Seriesは、どのChartAreaに表示するかを指定できる

Title、Legend、ChartArea、Seriesに主従関係が無く、Chartコントロールに同列でぶら下がっており、それぞれCollectionとして登録できる構造は、パッと見た感じ分かり難いかもしれません。

私も最初はこの構造の意味が分かりませんでした。

ChartAreaに、Title、Legend、Sereiesの登録メソッドが有れば分かりやすいのですが、そうではなかったからです。

では、これらをどうやってChartAreaに関連付けるかと言うと、答えはChartAreaのNameプロパティになります。

ChartAreaのNameプロパティに設定した名前を、Title、Legend、Seriesのオブジェクトに指定してあげることで、全てが関連づきます。

この仕組みを使う事で、1つのChartコントロールの中に、独立した複数のグラフを表示することができるようになります。

複数のChartコントロールを張り付ければ同じ事ができるので、この仕組みを使う機会は少ないとは思いますが、理解しておいた方が間違いが少ないでしょう。

サンプルソース1

それでは、次の線グラフを描画するためのサンプルソースで少し詳しく解説していきます。

今回は、コントロールに "uxChart1" という名前を付けています。

XAMLのソースコード

以下がXAMLのソースコードです。

<Window x:Class="MsChartTest.MainWindow"
        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:wfc="clr-namespace:System.Windows.Forms.DataVisualization.Charting;assembly=System.Windows.Forms.DataVisualization"
        xmlns:local="clr-namespace:MsChartTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="1320">
    <Grid>
        <WindowsFormsHost>
            <wfc:Chart x:Name="uxChart1"/>
        </WindowsFormsHost>
    </Grid>
</Window>

では、次にC#のソースコードです。

C#のソースコード

まず初めに、冒頭に次の2行を記述しておきます。

using System.Windows.Forms.DataVisualization.Charting;
using System.Drawing;

折れ線グラフのデータを作る部分、チャート全体の設定、折れ線グラフを描画するという3つの構成になっています。

//---------------------------------------------------
//折れ線グラフのデータ作成
//---------------------------------------------------
double[] xs = Enumerable.Range(0, 100).Select(i => (double)i).ToArray();
double[] ys1 = Enumerable.Range(0, 100).Select(i => Math.Sin(i * 0.1)).ToArray();
double[] ys2 = Enumerable.Range(0, 100).Select(i => 2 + Math.Cos(i * 0.1)).ToArray();
double[] ys3 = Enumerable.Range(0, 100).Select(i => 4 + Math.Sin(i * 0.1) * Math.Cos(i * 0.1)).ToArray();

//---------------------------------------------------
// チャート全体の設定
//---------------------------------------------------
//タイトル、Series、凡例をクリア
//初期状態としてChartAreaとSeriesが1つだけ登録されているが、複数のグラフを描画する際の
//ループ処理が面倒なので事前にクリアしておく
uxChart1.Titles.Clear();
uxChart1.Series.Clear();
uxChart1.Legends.Clear();
uxChart1.ChartAreas.Clear();

//チャートエリアを追加(名前は必須)
uxChart1.ChartAreas.Add("");

//タイトルを設定
uxChart1.Titles.Add("タイトル1");

//凡例の表示エリアを登録
uxChart1.Legends.Add("");

//---------------------------------------------------
// 折れ線グラフの描画
//---------------------------------------------------

//=========== 1つ目の線グラフを描画 ================
//Seriesを追加
var seri1 = new Series("凡例1") { ChartType = SeriesChartType.Line };
//Seriesを生成
Enumerable.Range(0, ys1.Length).Select(i => seri1.Points.AddXY(xs[i], ys1[i])).ToArray();
//ChartにSeriesを登録
uxChart1.Series.Add(seri1);

//=========== 2つ目の線グラフを描画 ================
//Seriesを追加
var seri2 = new Series("凡例2") { ChartType = SeriesChartType.Line };
//Seriesを生成
Enumerable.Range(0, ys2.Length).Select(i => seri2.Points.AddXY(xs[i], ys2[i])).ToArray();
//ChartにSeriesを登録
uxChart1.Series.Add(seri2);

//=========== 3つ目の線グラフを描画 ================
//Seriesを追加
var seri3 = new Series("凡例3") { ChartType = SeriesChartType.Line };
//Seriesを生成
Enumerable.Range(0, ys3.Length).Select(i => seri3.Points.AddXY(xs[i], ys3[i])).ToArray();
//ChartにSeriesを登録
uxChart1.Series.Add(seri3);

MSChartを使う時、最初にCollectionはクリアしておく

MSChartを張り付けた状態では、Legend、ChartArea、Seriesの各コレクションには、1つだけオブジェクトが登録された状態になっています。

わざわざオブジェクトを生成しなくても、次の様に添え字を使ってプロパティを設定すればグラフ描画が出来ますので、1つのグラフだけを描画するのであれば特に気にする必要はありません。

uxChart1.ChartArea[0].IsDockedInsideChartArea = true;

しかし、1つのMSChartコントロールに複数のグラフを表示し、しかもボタンを押して何度も再描画するような仕様の場合だと、「プログラム起動時の初回は、Titleだけ追加して、Legend、ChartArea、Seriesに初期状態で登録されているオブジェクトはそのまま使い、ボタンが押されたときはこれらを全てクリアして、Title、Legend、ChartArea、Seriesにオブジェクトを追加する」という場合分け処理が必要となってしまいます。

それならボタンが押されたタイミングでコレクションをクリアしておく方が、ループ内の場合分けをしなくて済む分楽になります。

チャート全体の設定でClear()メソッドを呼んだ後で、再度Addしてオブジェクトをコレクションに登録しているのはこのためです。

uxChart1.Titles.Clear();
uxChart1.Series.Clear();
uxChart1.Legends.Clear();
uxChart1.ChartAreas.Clear();

//チャートエリアを追加(名前は必須)
uxChart1.ChartAreas.Add("");

//タイトルを設定
uxChart1.Titles.Add("タイトル1");

//凡例の表示エリアを登録
uxChart1.Legends.Add("");

サンプルソース2

次は、ChartAreaとTitle、Legend、Seriesを関連付けて、1つのMSChartコントロールに複数のグラフを表示するサンプルを紹介します。

XAMLのソースコード

サンプル1のソースコードと全く同じです。

<Window x:Class="MsChartTest.MainWindow"
        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:wfc="clr-namespace:System.Windows.Forms.DataVisualization.Charting;assembly=System.Windows.Forms.DataVisualization"
        xmlns:local="clr-namespace:MsChartTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="1320">
    <Grid>
        <WindowsFormsHost>
            <wfc:Chart x:Name="uxChart1"/>
        </WindowsFormsHost>
    </Grid>
</Window>

C#のソースコード

ChartAreaを2つ用意し、”Area1”と”Area2”という名前を付けています。

そして、Title、Legend、Seriesについて、”Area1”と”Area2”のどちらに表示するかを、TitleとLegendについてはDockedToChartAreを、SeriesについてはChartAreaとLegendプロパティに設定しています。

Seriesオブジェクト1つに付き、どこのChartArea(グラフ表示エリア)にグラフを表示し、どこのLegend(凡例表示エリア)に凡例を表示するかを指定できるようになっている点にご注意下さい。

//---------------------------------------------------
//折れ線グラフのデータ作成
//---------------------------------------------------
double[] xs = Enumerable.Range(0, 100).Select(i => (double)i).ToArray();
double[] ys1 = Enumerable.Range(0, 100).Select(i => Math.Sin(i * 0.1)).ToArray();
double[] ys2 = Enumerable.Range(0, 100).Select(i => 2 + Math.Cos(i * 0.1)).ToArray();
double[] ys3 = Enumerable.Range(0, 100).Select(i => 4 + Math.Sin(i * 0.1) * Math.Cos(i * 0.1)).ToArray();

//---------------------------------------------------
// チャート全体の設定
//---------------------------------------------------
//タイトル、Series、凡例をクリア
//初期状態としてChartAreaとSeriesが1つだけ登録されているが、複数のグラフを描画する際の
//ループ処理が面倒なので事前にクリアしておく
uxChart11.Titles.Clear();
uxChart1.Series.Clear();
uxChart1.Legends.Clear();
uxChart1.ChartAreas.Clear();

//チャートエリアを追加(名前は必須)
uxChart1.ChartAreas.Add("Area1");
uxChart1.ChartAreas.Add("Area2");

//タイトルを設定
uxChart1.Titles.Add(new Title("タイトル1") { DockedToChartArea = "Area1",IsDockedInsideChartArea = false });
uxChart1.Titles.Add(new Title("タイトル2") { DockedToChartArea = "Area2", IsDockedInsideChartArea = false });

//凡例の表示エリアを登録
uxChart1.Legends.Add(new Legend("Area1") { DockedToChartArea = "Area1", IsDockedInsideChartArea = false });
uxChart1.Legends.Add(new Legend("Area2") { DockedToChartArea = "Area2", IsDockedInsideChartArea = false });

//---------------------------------------------------
// 折れ線グラフの描画
//---------------------------------------------------

//=========== 1つ目の線グラフを描画 ================
//Seriesを追加
var seri1 = new Series("凡例1") { ChartType = SeriesChartType.Line,ChartArea="Area1",Legend="Area1" };
//Seriesを生成
Enumerable.Range(0, ys1.Length).Select(i => seri1.Points.AddXY(xs[i], ys1[i])).ToArray();
//ChartにSeriesを登録
uxChart1.Series.Add(seri1);

//=========== 2つ目の線グラフを描画 ================
//Seriesを追加
var seri2 = new Series("凡例2") { ChartType = SeriesChartType.Line, ChartArea = "Area1", Legend = "Area1" };
//Seriesを生成
Enumerable.Range(0, ys2.Length).Select(i => seri2.Points.AddXY(xs[i], ys2[i])).ToArray();
//ChartにSeriesを登録
uxChart1.Series.Add(seri2);

//=========== 3つ目の線グラフを描画 ================
//Seriesを追加
var seri3 = new Series("凡例3") { ChartType = SeriesChartType.Line, ChartArea = "Area2", Legend = "Area2" };
//Seriesを生成
Enumerable.Range(0, ys3.Length).Select(i => seri3.Points.AddXY(xs[i], ys3[i])).ToArray();
//ChartにSeriesを登録
uxChart1.Series.Add(seri3);

まとめ

今回は Visual Studio のWindowsFormに標準搭載されているMSChart について、その構造とサンプルを交えた使い方について解説しました。

NuGetでインストールすることなく、参照設定だけで使えること、線グラフの表示が高速であること、3Dグラフが書けることがMSChartのメリットです。

クラス構造は少し特殊ですが、数多くの種類のグラフに対応しているので、特にビジネス用途では重宝すると思います。

WPFから使う場合、WindowsFormsHost を経由して利用する必要はありますが、XAMLに数行記述するだけでWindowsFormと同じように使えるので、是非挑戦してみて下さい。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次