MSChartのインストール方法や使い方については、こちらの記事に記載していますが、使用頻度が高いグラフを簡単に描画出来るように関数化(メソッド化)しました。
引数にMSChartのコントロールと描画したいデータを渡すとグラフ描画されます。
サンプルはWPFを使っていますが、関数(メソッド)部分はWindowsFormでもそのまま使える様になっています。
また、必要な修正がしやすい様に、最小限のプロパティを使って作っていますので、あとはご自身の用途に応じてカスタマイズしてお使いください。
グラフ(チャート)の種類
この関数(メソッド)を使えば、次のグラフ(チャート)が簡単に描画できるようになっています。
サンプルプログラム
サンプルプログラムをもとに、MSChartの使い方を解説していきたいと思います。
画面レイアウト
チャート1~チャート3までタブで分かれていて、合計10個のMSChartコントロールを張り付けています。
描画ボタンをクリックすると、全てのチャートが描画されます。
サンプルのダウンロード場所
サンプルプログラムのソースは下記からダウンロードできるようになっています。
MSChartsTestプロジェクト一式
関数(メソッド)の使い方
ScottPlotを関数化した時と同じ仕様にしていて、グラフ描画はDrowから始まるメソッド名になっています。
チャート(グラフ)名 | 関数(メソッド)名 | 内容 |
---|---|---|
折れ線グラフ | DrawLine | 単一の折れ線グラフを描画 |
折れ線グラフ(複数) | DrawLine | 複数の折れ線グラフを描画 |
散布図 | DrawScatt | 散布図を描画 |
円グラフ | DrawPie | 円グラフを描画 |
縦棒グラフ | DrawColumn | 縦棒グラフを描画 |
横棒グラフ | DrawBar | 横棒グラフを描画 |
棒グラフ(グルーピング) | DrawGroupColumn | 複数の棒グラフをグルーピングして描画 |
積み上げグラフ | DrawStack | 積み上げグラフを描画 |
レーダーチャート | DrawRadar | レーダーチャートを表示 |
箱ひげ図 | DrawPopulations | 箱ひげ図を描画 |
第一引数にコントロール、第二引数にタイトル、第三引数以降に描画データ(プロットデータ)を渡します。
コントロール名が "uxChart1" 、xs にX方向のデータ、ysにY方向のデータが格納されている場合は次の様記述します。
//グラフの描画
DrawLine(uxChart1,"折れ線グラフ",xs, ys);
第三引数であるプロットデータには、大きく分けて2種類の形式があります。
1つは、プロットデータの配列を渡す形式で、折れ線や棒グラフ、円グラフ、散布図の時に使用します。
1つは、複数の折れ線グラフ、グルーピングした棒グラフ、積み上げグラフ等のように、1つのチャートコントロールに対してグラフを複数描画する場合に使用し、こちらはタプルをリスト形式で渡すようになっています。
詳しくはソースコードを見て頂くとして、ここではポイントに絞って説明しておきたいと思います。
単独の折れ線グラフ、散布図
プロットデータを配列データ形式で渡します。
折れ線グラフの場合は次の様になります。
DrawLine(uxChart1,"折れ線グラフ",xs, ys);
箱ひげ図
箱ひげ図については、1次元配列のデータを引数に渡すことで描画が可能です。
DrawPopulations(uxChart1,"箱ひげ図",ys);
OxyPlotと比べて、MSChartでは単純に配列を渡すだけで、箱ひげ図に必要な統計情報を内部で自動計算してくれます。
その際、カスタムプロパティを使って平均値や中央値のラインを表示/非表示に設定したり、パーセンタイルの値を指定することができます。
カスタムプロパティはSeriesのインデクサという形で実装されていて、添え字の文字列を指定することで、必要な設定が出来る様になっています。
var seri = new Series() { ChartType = SeriesChartType.BoxPlot, IsVisibleInLegend = false };
seri["BoxPlotSeries"] = "PlotData01"; //箱ひげにデータを関連付ける
seri["BoxPlotShowAverage"] = "true"; //平均値のライン表示
seri["BoxPlotShowMedian"] = "true"; //中央値のライン表示
seri["BoxPlotShowUnusualValues"] = "true"; //異常値の値を表示
seri["BoxPlotPercentile"] = "25"; //パーセンタイル値の指定
seri["BoxPlotWhiskerPercentile"] = "10"; //ひげのパーセンタイル値
chart.Series.Add(seri);
縦棒グラフ、横棒グラフ
縦棒グラフ、横棒グラフともプロットデータを2つ渡しますが、こちらは第一引数が文字列で、第二引数が数値(double)です。
//棒グラフ
DrawBar(uxChart6, "横棒グラフ", labels, values);
円グラフ
円グラフは、ラベルと対応する値の配列を描画データとして渡すだけで表示してくれます。
//円グラフのデータ
double[] values = { 778, 43, 283, 76, 184 };
string[] labels = { "C#", "JAVA", "Python", "F#", "PHP" };
//円グラフの描画
DrawPie(uxChart4, "円グラフ", labels, values);
複数の折れ線グラフ
1つのグラフに必要な凡例、X座標、y座標のデータをタプルでまとめて、それをリストに登録したものを引数に渡します。
List<(string legend, double[] xs, double[] ys)> linedata = new List<(string legend, double[] xs, double[] ys)>();
linedata.Add(("legend1", xs, ys1));
linedata.Add(("legend2", xs, ys2));
linedata.Add(("legend3", xs, ys3));
DrawLine(uxChart2, "折れ線グラフ(複数)", linedata);
縦棒グラフ(グルーピング)、積み上げグラフ
これらは複数の値をグループ化して、まとめて表示するチャートであり、タプルのリスト形式でプロットデータを設定します。
グループ化が少し分かりづらいかもしれませんが、項目名と内訳と考えてもらえれば理解しやすいと思います。
ちなみに、この2つのグラフはデータの与え方が全く同じで、ChartTypeを変えるだけで縦棒グラフ(グルーピング)なったり、積み上げグラフになったりします。
ただ、積み上げグラフの場合は凡例が下から積みあがってしまうので、縦棒グラフ(グルーピング)の凡例の並びと合わせるため、関数の最初にデータの並びを逆転させています。
見て頂く左右のグラフの凡例の色が異なっていることが分かると思います(左はコアの色が青、右はコアの色がグレー)。
色も完全に統一したい場合は、バーのColorを指定する必要がありますが、今回のサンプルではそこまでしていません。
MSChartもLiveChartsやScottPlotと同様、項目名と内訳という考え方でプロットデータをセットすれば良いようになっています。
//グルーピングのデータ作成
string[] group = { "コア数", "クロック数", "スレッド数", "キャッシュ", "価格" };
List<(string, double[])> datas = new List<(string, double[])>();
datas.Add(("Core-i7", new double[] { 8, 3.6, 16, 64, 70 }));
datas.Add(("Core-i5", new double[] { 6, 2.8, 12, 32, 50 }));
datas.Add(("Core-i3", new double[] { 4, 2.0, 8, 16, 30 }));
datas.Add(("Pentium", new double[] { 2, 1.8, 4, 8, 10 }));
datas.Add(("Celeron", new double[] { 2, 1.5, 4, 8, 5 }));
//棒グラフ(グルーピング)
DrawGroupColumn(uxChart1, "グループ", group, datas);
ScottPlot、OxyPlot、LiveChartsとサンプルソースを合わせているため group という変数名を使っていますが、本当は明細(detail) とか、内訳(breakdown) という変数名にした方が良かったなと思っています。
ソースコード
サンプルプロジェクトをダウンロード&解凍したあと、プロジェクトをVisual Studioで開くと、次のレイアウトが表示されます。
タブページが3つあり、1ページ目と2ページ目には4つ、最後の3ページ目には1つ、合計10個のMSChartのコントロールを張り付けています。
デザイナー上ではグレーの箱として表示されていますが、これはMSChartコントロールではなく、WindowsFormsHostです。
しかし、WindowsFormsHostのサイズを広げると、自動的に MSChartのサイズも連動して広がるため、実質MSChartのサイズを直接広げている事と同じです。
サンプルプログラムのXAMLのソースコードは次の様になっています。
ちなみに、新しくソリューションを作って下記のソースコードを張り付けて使う場合は、WindowsFromsHost と MSChartが使えるようにするため、ソリューションエクスプローラーの「参照」に次の3つのアセンブリを追加しておいてください。
- System.Windows.Forms
- System.Windows.Forms.DataVisualization
- WindowsFormsIntegration
<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>
<TabControl>
<TabItem Header="チャート1">
<Grid>
<WindowsFormsHost Height="315" Width="300" Margin="22,46,990,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart1"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="343,46,669,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart2"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="665,46,347,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart3"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="989,46,23,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart4"/>
</WindowsFormsHost>
</Grid>
</TabItem>
<TabItem Header="チャート2">
<Grid>
<WindowsFormsHost Height="315" Width="300" Margin="22,46,990,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart5"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="343,46,669,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart6"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="665,46,347,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart7"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="989,46,23,58" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart8"/>
</WindowsFormsHost>
</Grid>
</TabItem>
<TabItem Header="チャート3">
<Grid>
<WindowsFormsHost Height="315" Width="519" Margin="22,46,0,30" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart9"/>
</WindowsFormsHost>
<WindowsFormsHost Height="315" Width="300" Margin="574,46,0,30" HorizontalAlignment="Left">
<wfc:Chart x:Name="uxChart10"/>
</WindowsFormsHost>
</Grid>
</TabItem>
</TabControl>
<Button Content="描画" HorizontalAlignment="Left" Margin="1212,28,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
次にC#のソースコードを見てみましょう。
ソースを読み解く上で、MSChartの描画手順の全体像を知っていると理解しやすいので、櫃能な方はこちらの記事もご参考にして下さい。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms.Integration;
using System.Windows.Forms.DataVisualization.Charting;
using System.Drawing;
namespace MsChartTest
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Random rand = new Random();
//折れ線グラフのデータ作成
double[] xs = Enumerable.Range(0, 100).Select(i => (double)i).ToArray();
double[] ys = Enumerable.Range(0, 100).Select(i => (double)(i + rand.Next(0, 100) * ((rand.Next(1, 2) == 1) ? 1 : -1))).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();
//グラフの描画
DrawLine(uxChart1, "折れ線グラフ", xs, ys);
List<(string legend, double[] xs, double[] ys)> linedata = new List<(string legend, double[] xs, double[] ys)>();
linedata.Add(("legend1", xs, ys1));
linedata.Add(("legend2", xs, ys2));
linedata.Add(("legend3", xs, ys3));
DrawLine(uxChart2, "折れ線グラフ(複数)", linedata);
//散布図
DrawScatt(uxChart3, "散布図", xs, ys);
//円グラフ
double[] values = { 778, 43, 283, 76, 184 };
string[] labels = { "C#", "JAVA", "Python", "F#", "PHP" };
DrawPie(uxChart4, "円グラフ", labels, values);
//縦棒グラフ
DrawColumn(uxChart5, "縦棒グラフ", labels, values);
//横棒グラフ
DrawBar(uxChart6, "横棒グラフ", labels, values);
//グルーピングのデータ作成
string[] items = { "コア数", "クロック数", "スレッド数", "キャッシュ", "価格" };
List<(string, double[])> datas = new List<(string, double[])>();
datas.Add(("Core-i7", new double[] { 8, 3.6, 16, 64, 70 }));
datas.Add(("Core-i5", new double[] { 6, 2.8, 12, 32, 50 }));
datas.Add(("Core-i3", new double[] { 4, 2.0, 8, 16, 30 }));
datas.Add(("Pentium", new double[] { 2, 1.8, 4, 8, 10 }));
datas.Add(("Celeron", new double[] { 2, 1.5, 4, 8, 5 }));
//棒グラフ(グルーピング)
DrawGroupColumn(uxChart7, "グループ", items, datas);
//積み上げグラフ
DrawStack(uxChart8, "積み上げ", items, datas);
//レーダーチャート
DrawRadar(uxChart9, "レーダー", items, datas);
//箱ひげ図
DrawPopulations(uxChart10, "箱ひげ", ys);
}
/// <summary>
/// 折れ線グラフ(単独)
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="xs"></param>
/// <param name="ys"></param>
private void DrawLine(Chart chart, string title, double[] xs, double[] ys)
{
ChartInit(chart, title);
Series seri = new Series() { ChartType = SeriesChartType.Line, IsVisibleInLegend = false };
Enumerable.Range(0, ys.Length).Select(i => seri.Points.AddXY(xs[i], ys[i])).ToArray();
chart.Series.Add(seri);
}
/// <summary>
/// 折れ線グラフ(複数)
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="datas"></param>
private void DrawLine(Chart chart, string title, List<(string legend, double[] xs, double[] ys)> datas)
{
ChartInit(chart, title);
foreach (var data in datas)
{
Series seri = new Series(data.legend) { ChartType = SeriesChartType.Line };
Enumerable.Range(0, data.ys.Length).Select(i => seri.Points.AddXY(data.xs[i], data.ys[i])).ToArray();
chart.Series.Add(seri);
}
}
/// <summary>
/// 散布図
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="xs"></param>
/// <param name="ys"></param>
private void DrawScatt(Chart chart, string title, double[] xs, double[] ys)
{
ChartInit(chart, title);
Series seri = new Series() { ChartType = SeriesChartType.FastPoint, IsVisibleInLegend = false };
Enumerable.Range(0, ys.Length).Select(i => seri.Points.AddXY(xs[i], ys[i])).ToArray();
seri.MarkerStyle = MarkerStyle.Circle;
chart.Series.Add(seri);
}
/// <summary>
/// 円グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawPie(Chart chart, string title, string[] labels, double[] values)
{
ChartInit(chart, title);
Series seri = new Series() { ChartType = SeriesChartType.Pie };
Enumerable.Range(0, values.Length).Select(i => seri.Points.AddXY(labels[i], values[i])).ToArray();
chart.Series.Add(seri);
}
/// <summary>
/// 縦棒グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="ys"></param>
private void DrawColumn(Chart chart, string title, string[] labels, double[] ys)
{
ChartInit(chart, title,false);
Series seri = new Series() { ChartType = SeriesChartType.Column,IsVisibleInLegend = false };
Enumerable.Range(0, ys.Length).Select(i => seri.Points.AddXY(labels[i], ys[i])).ToArray();
chart.Series.Add(seri);
}
/// <summary>
/// 横棒グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="ys"></param>
private void DrawBar(Chart chart, string title, string[] labels, double[] ys)
{
ChartInit(chart, title, false);
Series seri = new Series() { ChartType = SeriesChartType.Bar, IsVisibleInLegend = false };
Enumerable.Range(0, ys.Length).Select(i => seri.Points.AddXY(labels[i], ys[i])).ToArray();
chart.Series.Add(seri);
}
/// <summary>
/// 箱ひげ図
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="ys"></param>
private void DrawPopulations(Chart chart, string title, double[] ys)
{
ChartInit(chart, title, false);
//データ保持専用のシリーズを作成し、データを登録
var data_seri = new Series("PlotData01") { Enabled = false };
Enumerable.Range(0,ys.Length).Select(i=>data_seri.Points.Add(i)).ToArray();
chart.Series.Add(data_seri);
//BoxPlotのシリーズを作成し、カスタムプロパティを設定
var seri = new Series() { ChartType = SeriesChartType.BoxPlot, IsVisibleInLegend = false };
seri["BoxPlotSeries"] = "PlotData01"; //箱ひげにデータを関連付ける
seri["BoxPlotShowAverage"] = "true"; //平均値のライン表示
seri["BoxPlotShowMedian"] = "true"; //中央値のライン表示
seri["BoxPlotShowUnusualValues"] = "true"; //異常値の値を表示
seri["BoxPlotPercentile"] = "25"; //パーセンタイル値の指定
seri["BoxPlotWhiskerPercentile"] = "10"; //ひげのパーセンタイル値
chart.Series.Add(seri);
//カスタムプロパティの内容が書かれているURL
//http://www.gss-updates.com/site/gabhelpen/custom_properties.htm
}
/// <summary>
/// 縦棒グラフ(グループ)
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawGroupColumn(Chart chart, string title, string[] labels, List<(string legend, double[] ys)> values)
{
ChartInit(chart, title, false);
//labelの分だけルール
for (int i = 0; i < labels.Length; i++)
{
//シリーズの生成
var seri = new Series(labels[i]) { ChartType = SeriesChartType.Column, IsVisibleInLegend = true };
//表示対象のデータを values から取り出してシリーズに登録
for (int x = 0; x < values.Count; x++)
{
DataPoint dp = new DataPoint();
dp.SetValueXY(values[x].legend, values[x].ys[i]);
seri.Points.Add(dp);
}
//チャートにシリーズを登録
chart.Series.Add(seri);
}
}
/// <summary>
/// 積み上げグラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawStack(Chart chart, string title, string[] labels, List<(string legend, double[] ys)> values)
{
ChartInit(chart, title, false);
//積み上げ方向を反転(先頭項目が一番上に来るように)
labels = labels.Reverse().ToArray();
values = values.Select(i =>(i.legend,i.ys.Reverse().ToArray())).ToList();
//labelの分だけルール
for(int i = 0;i < labels.Length;i ++)
{
//シリーズの生成
var seri = new Series(labels[i]) { ChartType = SeriesChartType.StackedColumn, IsVisibleInLegend = true };
//表示対象のデータを values から取り出してシリーズに登録
for (int x = 0; x < values.Count; x++)
{
DataPoint dp = new DataPoint();
dp.SetValueXY(values[x].legend, values[x].ys[i]);
seri.Points.Add(dp);
}
//チャートにシリーズを登録
chart.Series.Add(seri);
}
}
/// <summary>
/// レーダーチャート
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawRadar(Chart chart, string title, string[] labels, List<(string legend, double[] ys)> values)
{
ChartInit(chart, title, false);
//labelの分だけルール
for (int i = 0; i < values.Count; i++)
{
//シリーズの生成
var seri = new Series(values[i].legend) { ChartType = SeriesChartType.Radar, IsVisibleInLegend = true };
for (int j = 0; j < labels.Length; j++)
{
DataPoint dp = new DataPoint();
dp.SetValueXY(labels[j], values[i].ys[j]);
seri.Points.Add(dp);
}
//チャートにシリーズを登録
chart.Series.Add(seri);
}
}
/// <summary>
/// チャートの初期化
/// </summary>
/// <param name="chart"></param>
private void ChartInit(Chart chart, string title,bool Zoom = true)
{
//タイトル/エリア/シリーズのクリア
chart.Titles.Clear();
chart.ChartAreas.Clear();
chart.Series.Clear();
chart.Legends.Clear();
//タイトルの設定
chart.Titles.Add(title);
//凡例表示エリアの登録
chart.Legends.Add("");
//チャートエリアの生成と登録
var area = new ChartArea();
chart.ChartAreas.Add(area);
//X軸とY軸のオブジェクトを取得
var axis_x = area.AxisX;
var axis_y = area.AxisY;
//X軸の補助線を設定
axis_x.MajorGrid.LineColor = System.Drawing.Color.LightGray;
axis_x.MinorGrid.LineColor = System.Drawing.Color.LightGray;
axis_x.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
//Y軸の補助線を設定
axis_y.MajorGrid.LineColor = System.Drawing.Color.LightGray;
axis_y.MinorGrid.LineColor = System.Drawing.Color.LightGray;
axis_y.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
//ズーム機能を有効化
axis_x.ScaleView.Zoomable = Zoom;
axis_y.ScaleView.Zoomable = Zoom;
//ズーム機能を実現するためのイベントハンドラ定義
if (Zoom)
{
//マウスホイールボタンのクリックによるズーム解除
chart.MouseClick += (s, e) =>
{
if (e.Button == System.Windows.Forms.MouseButtons.Middle)
{
axis_x.ScaleView.ZoomReset();
axis_y.ScaleView.ZoomReset();
}
};
//マウスホィールによるズーム処理
chart.MouseWheel += (s, e) =>
{
try
{
double xmin = axis_x.ScaleView.ViewMinimum;
double xmax = axis_x.ScaleView.ViewMaximum;
double xpos = axis_x.PixelPositionToValue(e.Location.X);
double xsize = (xmax - xmin) * ((e.Delta > 0) ? 0.25 : 1);
axis_x.ScaleView.Zoom(Math.Round(xpos - xsize, 0, MidpointRounding.AwayFromZero), Math.Round(xpos + xsize, 0, MidpointRounding.AwayFromZero));
double ymin = axis_y.ScaleView.ViewMinimum;
double ymax = axis_y.ScaleView.ViewMaximum;
double ypos = axis_y.PixelPositionToValue(e.Location.Y);
double ysize = (ymax - ymin) * ((e.Delta > 0) ? 0.25 : 1);
axis_y.ScaleView.Zoom(Math.Round(ypos - ysize, 0, MidpointRounding.AwayFromZero), Math.Round(ypos + ysize, 0, MidpointRounding.AwayFromZero));
}
catch { }
};
}
}
}
}
ソースの解説(ポイント)
使い方については、ソースコードを見て頂くとして、ここではポイントだけ解説しておきます。
MSChartのグラフ描画で一番簡単なサンプルは次の通りです。
//タイトルを設定
uxChart.Titles.Add("タイトル1");
//Seriesを追加
uxChart.Series[0].LegendText = "凡例1";
//グラフの種類を設定
uxChart.Series[0].ChartType = SeriesChartType.Line;
//描画データを登録
Enumerable.Range(0, ys.Length).Select(i => uxChart.Series[0].Points.AddXY(xs[i], ys[i])).ToArray();
このサンプルは折れ線グラフを描画する場合のものですが、表示したいグラフに応じてChartStyleを設定することで、色々なグラフが描画できるようになっています。
縦軸・横軸の設定について
縦軸、横軸は ChartAreaのAxisX,AxisYプロパティを使います。
//チャートエリアの生成
var area = new ChartArea(title);
//X軸の補助線を設定
area.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
area.AxisX.MinorGrid.LineColor = System.Drawing.Color.LightGray;
area.AxisX.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
//Y軸の補助線を設定
area.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
area.AxisY.MinorGrid.LineColor = System.Drawing.Color.LightGray;
area.AxisY.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
積み上げグラフについて
MSChartの場合、積み上げグラフはChartStyle に ChartStyle.StackedColumnを代入することで表示できます。
データの与え方やグラフの表示手順は縦棒グラフ(グループ)と全く同じですが、積み上がる方向が下から上になってしまいます。
凡例を縦棒グラフ(グループ)と同じ様にするために、次の2行で与えるデータの順番を逆転させています。
//積み上げ方向を反転(先頭項目が一番上に来るように)
labels = labels.Reverse().ToArray();
//凡例の並びを反転(トップがコア数)
values = values.Select(i =>(i.legend,i.ys.Reverse().ToArray())).ToList();
//データの積み上げを反転(トップがコア数)
チャートの初期化
MSChartコントロールの見栄えや操作性を良くするためには、補助線の色をグレーにしたり、マウスホィールによる拡大縮小に対応させたりなど、ある程度の設定が必要になります。
個々の関数(メソッド)内に記述するにはあまりにも量が多いので、初期化処理を集めた ChartInit 関数を用意しています。
中身は、各Collectionのクリアと最低限必要なオブジェクト(Title、ChartArea)の追加、XY軸の設定、拡大縮小用マウスイベントのハンドラ登録などです。
マウスホィールを回すとグラフが拡大/縮小し、中央ボタンをクリックすると解除される仕様です。
現時点ではマウスホィールの回転に応じて、縦横どちらも拡大/縮小されてしまうため、少々使い勝手が悪いと思います。
どちらか一方を固定してしまうとか、別の操作と併用して拡大/縮小を縦横独立して操作できるようにするなどの工夫が必要かもしれません。
private void ChartInit(Chart chart, string title,bool Zoom = true)
{
//タイトル/エリア/シリーズのクリア
chart.Titles.Clear();
chart.ChartAreas.Clear();
chart.Series.Clear();
chart.Legends.Clear();
//タイトルの設定
chart.Titles.Add(title);
//凡例表示エリアの登録
chart.Legends.Add("");
//チャートエリアの生成と登録
var area = new ChartArea();
chart.ChartAreas.Add(area);
//X軸とY軸のオブジェクトを取得
var axis_x = area.AxisX;
var axis_y = area.AxisY;
//X軸の補助線を設定
axis_x.MajorGrid.LineColor = System.Drawing.Color.LightGray;
axis_x.MinorGrid.LineColor = System.Drawing.Color.LightGray;
axis_x.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
//Y軸の補助線を設定
axis_y.MajorGrid.LineColor = System.Drawing.Color.LightGray;
axis_y.MinorGrid.LineColor = System.Drawing.Color.LightGray;
axis_y.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
//ズーム機能を有効化
axis_x.ScaleView.Zoomable = Zoom;
axis_y.ScaleView.Zoomable = Zoom;
//ズーム機能を実現するためのイベントハンドラ定義
if (Zoom)
{
//マウスホイールボタンのクリックによるズーム解除
chart.MouseClick += (s, e) =>
{
if (e.Button == System.Windows.Forms.MouseButtons.Middle)
{
axis_x.ScaleView.ZoomReset();
axis_y.ScaleView.ZoomReset();
}
};
//マウスホィールによるズーム処理
chart.MouseWheel += (s, e) =>
{
try
{
double xmin = axis_x.ScaleView.ViewMinimum;
double xmax = axis_x.ScaleView.ViewMaximum;
double xpos = axis_x.PixelPositionToValue(e.Location.X);
double xsize = (xmax - xmin) * ((e.Delta > 0) ? 0.25 : 1);
axis_x.ScaleView.Zoom(Math.Round(xpos - xsize, 0, MidpointRounding.AwayFromZero), Math.Round(xpos + xsize, 0, MidpointRounding.AwayFromZero));
double ymin = axis_y.ScaleView.ViewMinimum;
double ymax = axis_y.ScaleView.ViewMaximum;
double ypos = axis_y.PixelPositionToValue(e.Location.Y);
double ysize = (ymax - ymin) * ((e.Delta > 0) ? 0.25 : 1);
axis_y.ScaleView.Zoom(Math.Round(ypos - ysize, 0, MidpointRounding.AwayFromZero), Math.Round(ypos + ysize, 0, MidpointRounding.AwayFromZero));
}
catch { }
};
}
}
まとめ
今回はMSChartでよく使うであろう10個のグラフについて、簡単に使えるような関数(メソッド)を作り、それに対する解説を行いました。
MSChartは標準搭載されている機能ですが、WPFで使う場合は WindowsFormsHostの中に入れて使う必要があります。
デザイナーに張り付ける方法以外はWindowsFormと全く同じ手順でグラフ描画が可能です。
MSChartには他にも多くのグラフに対応していますので、今回の記事を参考に必要な機能を追加して頂ければと思います。
コメント