【c#】円グラフを作成する方法

C#

C#で円グラフを作成する方法を紹介します。
本記事はVisualStudioで「Windowsフォームアプリケーション(.NET Framework)」の開発を行っている方を想定しています。

サンプルの説明

本記事ではここで紹介する仕様の円グラフを作成する手順を示します。

円グラフへプロットするデータ

円グラフへプロットするデータは下記のようなDataTableに保管されているものとします。

グラフへプロットするデータ

円グラフの仕様

  1. DataTableで定義したのValue列の値が大きい順に円グラフへプロットする。Value列の値が同じレコード同士はName列で昇順に並び替えて順番を決定する。
  2. レコードの上位10位より下の項目は「その他」項目として合計値を合算して表示する。※凡例の値が多くなりすぎると円グラフの見栄えが悪くなることを考慮し、表示できる最大の項目数を11としている。
  3. 凡例の項目名が長すぎると見栄えが悪くなるので、項目名が20文字より多くなる場合には最初の20文字を出力する。
  4. 出力する項目の文字列の幅と高さを計算し、凡例の高さと幅を設定する。
完成イメージ

円グラフを用意と凡例の書式を設定する

1.ツールボックスからChart要素をデザイン画面のフォームへドラッグ&ドロップしてください。

2.追加したchartを選択し、”Series”プロパティ横の[…]をクリックしてください。

3.”ChartType”プロパティの値を”Pie”に変更してください。

4.”CustomProperties”の左横の”>”をクリックします。

5.”PieLabelStyle”プロパティの値を”Disabled”を設定します。

(補足)
PieLabelStyleプロパティは、円グラフ上での項目名を表示を制御するプロパティです。下記はPieLabelStyleプロパティを”Inside”とした例です。”Disabled”にすると円グラフ上に項目名は表示されません。

PieLabelStyleプロパティを”Inside”とした例

6.”PieStartAngle”プロパティの値へ”270″を設定します。

7.”LegendText”プロパティにカーソルをあて(①)、右横の[…]をクリックします(②)。※”LegendText”プロパティは凡例のフォーマットを設定するプロパティです。

8.[新しいキーワードの挿入(I)]をクリックします。

9.キーワード(k)から”X値”を選択し(①)、[OK]をクリックします(②)。

10.半角スペース” “を入力し(①)、[新しいキーワードの挿入(I)]をクリックします。

11.キーワード(k)から”Y値”を選択し(①)、[OK]をクリックします(②)。

12.” %”と入力し(①)、[OK]をクリックします(②)。

13.”XValueType”プロパティの値を”String”に設定し(①)[OK]をクリックします(②)。

(補足)
“XValueType”プロパティの値を”String”にすることにより、凡例にXの値を使用するときにプロットした値が正確に表示されるようになります。デフォルト値の”Auto”のままだと数値(0など)で表示されてしまいます。

“XValueType”プロパティの設定に関する補足

14.これで下記のような円グラフが用意されるかと思います(幅や高さは別途調整しています)。

円グラフと凡例の配置を調整する

まずは円グラフ及び凡例の配置方法について解説します。
※ここの説明は執筆時点で筆者が認識している内容であり内容を保証するものではありません。ご自身で動作確認していただければ幸いです。

円グラフの配置は下記のように2つの四角形の領域を用いて定義します。それぞれの領域をChartAreaのPositionプロパティとInnerPlotPositionプロパティで指定することになります。PositionプロパティはChartの外枠からの相対位置を指定し、InnerPlotPositionプロパティはPositionプロパティで指定する領域からの相対位置を指定します。

凡例も同様に四角形領域を用いて定義します。LegendsのPositionプロパティでChartの外枠からの相対位置を指定します。

PositionプロパティとInnerPlotPositionプロパティのイメージ

PositionプロパティおよびInnerPlotPositionプロパティはそれぞれHeight、Width、X、Yに値を入力して配置を決定することになります。

  • Height:四角形領域の高さ。相対位置の基準となる四角形領域の高さに対しての比率(0~100)を指定する。
  • Width:四角形領域の幅。相対位置の基準となる四角形領域の幅に対しての比率(0~100)を指定する。
  • X:四角形領域の左上のX座標。相対位置の基準となる四角形領域の左上を基点としたX座標を指定する。
  • Y:四角形領域の左上のY座標。相対位置の基準となる四角形領域の左上を基点としたY座標を指定する。
PositionプロパティとInnerPlotPositionプロパティの説明

1.円グラフを用意と凡例の書式を設定するで作ったchartを選択し、”ChartAreas”プロパティ横の[…]をクリックしてください。

2.Positionプロパティの値を変更します。AutoをFalse、Heightを100、Widthを100、Xを0、Yを0に設定してください。

3.InnerPlotPositionプロパティの値を変更します。AutoをFalse、Heightを100、Widthを50、Xを5、Yを0に設定してください。

4.[OK]をクリックしてください。

5.chartを選択し、”Legends”プロパティ横の[…]をクリックしてください。

6.Positionプロパティの値を変更します。AutoをFalse、Xを60、Yを0に設定してください。※下記の画像ではHeightを80、Widthを40としていますが、これらの値はこの後でプログラムで設定し直すので任意の値でかまいません。

7.[OK]をクリックしてください。

プログラムの記述

下記のソースコードをLoadイベントなどに張り付けてください。プロットするデータはデータベースから取得した値を加工したりすることが多いかと思うので、プログラムで制御した方が便利かと思います。
プログラムの内容はコメントに記載しておりますのでご確認をお願いします。

//テーブルの作成し、グラフへプロットするデータを保管する。
System.Data.DataTable Table = new System.Data.DataTable();
Table.Columns.Add("ID", typeof(int));
Table.Columns.Add("Name", typeof(string));
Table.Columns.Add("Value", typeof(double));

Table.Rows.Add(new Object[] { 1, "AAA", 120 });
Table.Rows.Add(new Object[] { 2, "BBB", 220 });
Table.Rows.Add(new Object[] { 3, "CCCCC", 60 });
Table.Rows.Add(new Object[] { 4, "DD", 430 });
Table.Rows.Add(new Object[] { 5, "E", 800 });
Table.Rows.Add(new Object[] { 6, "FFFFFFF", 360 });
Table.Rows.Add(new Object[] { 7, "GGGG", 990 });
Table.Rows.Add(new Object[] { 8, "HHHHHHHHHHH", 150 });
Table.Rows.Add(new Object[] { 9, "IIII", 360 });
Table.Rows.Add(new Object[] { 10, "JJJJJJJ", 960 });
Table.Rows.Add(new Object[] { 11, "K", 740 });
Table.Rows.Add(new Object[] { 12, "L", 960 });
Table.Rows.Add(new Object[] { 13, "MMM", 780 });
Table.Rows.Add(new Object[] { 14, "N", 800 });
Table.Rows.Add(new Object[] { 15, "OOOOOOOO", 690 });

//テーブルのレコードの並び替えを行う。①Value列で降順に並び替える。②Name列で昇順に並び替える。
DataRow[] Rows = Table.Select("", "Value DESC, Name ASC");

//Velue列の合計値を算出する。この値は円グラフにプロットする比率の計算に使用する。
double sum = Convert.ToDouble(Table.Compute("Sum(Value)", null));

long maxSize = 0; //←凡例に出力する文字列で最も長い値を格納する。この値は凡例の幅の設定に用いる。
long sumRowHeight = 0;//←凡例に出力する文字列の高さの合計を格納する。この値は凡例の高さの設定に用いる。
long othersTime = 0; //Value列の値の上位10位より下の項目の合計値を格納する。この値は「その他」項目の計算用の計算に用いる。

for (int i1 = 0; i1 < Rows.Count() - 1; i1++)
{
    if (i1 < 10)
    {

        //グラフへプロットする比率を計算する。
        //※四捨五入の誤差の影響で出力した比率の合計が100%にならないことがあります。その旨は操作画面上などで説明するか別途対策を行う必要があると思います。
        double rate = Math.Round(Convert.ToDouble(Rows[i1][2]) / sum, 3, MidpointRounding.AwayFromZero) * 100;

        //項グラフへプロットする項目名を取得する。項目名が20文字以上の場合は先頭の20文字とする。
        string itemName = (Rows[i1][1].ToString().Length > 20) ? Rows[i1][1].ToString().Substring(0, 20) : Rows[i1][1].ToString();

        //グラフにデータをプロットする。
        System.Windows.Forms.DataVisualization.Charting.DataPoint dp = new System.Windows.Forms.DataVisualization.Charting.DataPoint();
        dp.SetValueXY(itemName, rate);
        chart1.Series["Series1"].Points.Add(dp);

        //凡例のサイズ計算を行う。
        maxSize = Math.Max(maxSize, TextRenderer.MeasureText(itemName, this.chart1.Legends["Legend1"].Font).Width);
        sumRowHeight += TextRenderer.MeasureText(itemName, this.chart1.Legends["Legend1"].Font).Height;

    } else {

        //上位10位より下の項目の合計値をまとめる。
        othersTime += Convert.ToInt32(Rows[i1][2]);

    }

}

//Value列の値の上位10位より下の項目の合計値は「その他」としてまとめて表示する。
if (othersTime > 0)
{

    //凡例のサイズ計算を行う。
    maxSize = Math.Max(maxSize, TextRenderer.MeasureText("その他", this.chart1.Legends["Legend1"].Font).Width);
    sumRowHeight += TextRenderer.MeasureText("その他", this.chart1.Legends["Legend1"].Font).Height;

    //グラフにデータをプロットする。
    double othersRate = Math.Round(othersTime / sum, 3, MidpointRounding.AwayFromZero) * 100;
    System.Windows.Forms.DataVisualization.Charting.DataPoint dp = new System.Windows.Forms.DataVisualization.Charting.DataPoint();
    dp.SetValueXY("その他", othersRate);
    chart1.Series["Series1"].Points.Add(dp);

}

//凡例の幅と高さを設定する。chart外枠との比率を設定する。
float width = (float)(maxSize + 100) / (float)this.chart1.Width * 100; //余白と色箇所分として100をプラスしている。単位を%にするため100を乗ずる。
float height = (float)(sumRowHeight + 40) / (float)this.chart1.Height * 100; //※余白分で40をプラスしている。単位を%にするため100を乗ずる。

this.chart1.Legends["Legend1"].Position.Width = width;
this.chart1.Legends["Legend1"].Position.Height = height;

これで冒頭で紹介した仕様のグラフが出来上がるかと思います。

完成!

以上となります。参考になれば幸いです。

タイトルとURLをコピーしました