C#アプリのローカルReporting ToolとしてChart.jsを使う

TL;DR

C#アプリからブラウザーを起動させ、http/httpsのWebサーバーが不要で、Chart.jsを使ってチャートを表示することができる。
1.チャート表示用html + 2.データロード用js + 3.データ定義js
1と2は一回作っておけばOKで、3はチャート表示する度にC#アプリから書き出す。
webpackは必須ではないが、TypeScriptでChart.jsを使うためにはimportが必要なため、通常はWebサーバーが必要だが、webpackで2.データロード用jsとChart.jsをバンドルにすればWebサーバーなしでfile:///プロトコルでも動作。

目次

背景

.Net Coreになってからすぐに使える内蔵のレポーティングツールがなく、.Net 5もそうみたい。
いろいろ調べたら第三者の有料のものが多くて何か面倒そう。
そこで目にしたのはChart.js である。ビジュアルがキレイでチャートの種類も豊富。無料で使えてドキュメントを読む限りシンプルに使えそうだから、これを使うことにした。
JavaScriptのライブラリだから、C#からはそのまま使えないが、少し工夫すれば普通に使えて重宝している。

全体像

全体像









使用ツール

  • Visual Studio Code(vscode)
    HTML/JavaScript/TypeScript編集
  • Node.js
    npmを使うため
  • webpack
    npmよりインストール
    TypeScriptの恩恵を受けるのにChart.jsimportすることが必要。importfile:///プロトコルの場合がブラウザーサポートされていない。そのためwebpackでindex.jsChart.jsのコードをバンドル(main.js)にして、HTMLの<Script>タグで指定すれば解決。
  • Chart.js
    npmよりインストール
    チャートを描画するjsライブラリ。
  • Visual Studio 2019 Community
    C# デモプロジェクト用、これに限らない。
  • ブラウザー
    HTMLを表示用、今回はMicrosoft Edge(chromium)を使用。

開発環境の準備

  1. プロジェクトフォルダー作成
    CsChartjsDemoというフォルダーを作成し、その中にsrcdistのフォルダーをそれぞれ作成する。
    CsChartjsDemo │ ├─dist #webpackの出力、htmlファイル、入力データjsファイル格納 └─src #ts,jsソースファイル格納
  2. package.json作成
    npm init -y
  3. TypeScript初期化
    tsc -init
  4. webpackをインストール
    npm install webpack webpack-cli --save-dev
  5. Chart.jsをインストール
    npm install chart.js

これでpackage.jsonはこんな感じになる:

{ "name": "cschartjsdemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^5.58.0", "webpack-cli": "^4.9.0" }, "dependencies": { "chart.js": "^3.5.1" } }

まずJavaScript + Chart.jsで動くようにする

※ここの説明をシンプルにするために、各ファイル名はwebpackがデフォルトで認識できるものにしている。

  1. チャート表示用HTMLファイル作成
    ./distフォルダーにindex.htmlを作成する。チャートを表示するためだけなので、Scriptタグ以外はほぼ空っぽ。
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" charset="UTF-8"> </head> <body> <script src="./main.js"></script> </body> </html>
  1. index.tsでチャート表示機能を実装
    ./src/index.ts
import Chart from "chart.js/auto"; //参照:https://www.chartjs.org/docs/latest/getting-started/integration.html //bodyを取得してcanvasを作成して挿入 let body: HTMLBodyElement = document.querySelector("body")!; let canvas: HTMLCanvasElement = document.createElement("canvas"); body.appendChild(canvas); //テスト用の入力データを作成、次のステップはこのデータをC#から書き出す let dataInput={ labels: ["1月","2月","3月"], datasets: [ { label: "売上", data: [100, 300, 500], backgroundColor:"LightBlue" }, { label: "仕入", data: [30, 100, 150], backgroundColor:"LightGreen" } ] }; //ここで問題なく表示できたら,dataInputをC#で生成させればOK let chartConfig= { type: "bar", data: dataInput }; let ctx = canvas.getContext("2d"); let chart: Chart = new Chart(ctx, chartConfig);
  1. tscでindex.tsindex.jsに出力
    tsc
  2. webpackでmain.jsを出力
    npx webpack

ファイルエクスプローラーで./dist/index.htmlをEdgeで開いてみる:

js単体でチャート表示


C#アプリからChart.js用入力データを生成してチャートを表示させる

順調にチャート表示できたので、ここからは上記手動でindex.tsの中で定義したdataInputをC#から書き出せばいい。

  1. C#でサンプルデータを作成する
    以下のようなDataTableを元データのサンプルとして作る

    string int int
    売上 仕入
    1月 100 30
    2月 300 100
    3月 500 150
    private DataTable CreateSampleTable() {//元データサンプルを作る DataTable dt = new DataTable(); dt.Columns.Add("月", typeof(string)); dt.Columns.Add("売上", typeof(int)); dt.Columns.Add("仕入", typeof(int)); dt.Rows.Add(new object[] { "1月", 100, 30 }); dt.Rows.Add(new object[] { "2月", 300, 100 }); dt.Rows.Add(new object[] { "3月", 500, 150 }); return dt; }
  2. C#でChart.jsの入力データモデルを定義する datadatasetの2種類:

    public class ChartJsDataSet {//Chart.js入力データのdatasetモデルを定義する public string label { get; set; } public List<int> data { get; set; } public string backgroundColor { get; set; } } public class ChartJsInputData {//Chart.js入力データのdataモデルを定義する public List<string> labels { get; set; } public List<ChartJsDataSet> datasets { get; set; } }
  3. C#でデータを定義したモデル形式に抽出し、inputData.jsに書き出す

    //まず元データのサンプルを作る var dt = CreateSampleTable(); //売上と仕入のdatasetをそれぞれ作る ChartJsDataSet cdsSales = new ChartJsDataSet() { label = "売上", data = dt.AsEnumerable().Select(r => r.Field<int>("売上")).ToList(), backgroundColor = "Gold" }; ChartJsDataSet cdsPurchase = new ChartJsDataSet() { label = "仕入", data = dt.AsEnumerable().Select(r => r.Field<int>("仕入")).ToList(), backgroundColor = "Pink" }; //datasetをListにする List<ChartJsDataSet> chartDatasets = new List<ChartJsDataSet>(); chartDatasets.Add(cdsSales); chartDatasets.Add(cdsPurchase); //datasetのListをinputDataモデルに入れる ChartJsInputData chartInput = new ChartJsInputData() { labels = dt.AsEnumerable().Select(r => r.Field<string>("月")).ToList(), datasets=chartDatasets }; //inputDataをjsonのstringに出力する string strChartInput = JsonSerializer.Serialize(chartInput, new JsonSerializerOptions() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All) }); //inputDataをjsファイルに書き出す string output = $"var dataInput={strChartInput};"; System.IO.File.WriteAllText($"{PROJ_PATH}\\dist\\dataInput.js",output);

    書き出されたdataInput.jsはこんな感じ:

    var dataInput={"labels":["1月","2月","3月"],"datasets":[{"label":"売上","data":[100,300,500],"backgroundColor":"Gold"},{"label":"仕入","data":[30,100,150],"backgroundColor":"Pink"}]};
  4. index.tsのなかのdataInputの定義を削除する

  5. index.htmldataInput.jsを追加する

    <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" charset="UTF-8"> </head> <body> <!-- main.jsの前にC#から書き出されたdataInput.jsを追加する --> <script src="./dataInput.js"></script> <script src="./main.js"></script> </body> </html>
  6. 再度tscを実行して、npx webpackを実行する
    ここからはindex.html,index.tsを編集する必要がなくなる。

  7. 最後にC#より以下のコードを実行すればデフォルトのブラウザーでindex.htmlを開く

    System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() { FileName = $"{PROJ_PATH}\\dist\\index.html", UseShellExecute = true }) ;

C#の出力データでチャート表示


後書き

以上はC#からChart.jsのチャートを生成する一番基本的なステップでした。
より高度なチャート機能を使うにはC#でのモデル定義追加や、index.tsでチャートをクリックしたときのイベントハンドル等、いろいろいじれて楽しそうです。
デスクトップのアプリでもJavascriptのライブラリが使えて便利な時代と実感しています。
上記のDemoでは、tscの際のエラー等、完璧ではありませんが、やり方のメモとしては支障がないと、また別件として今後対応していきたいと思います。

コメント

このブログの人気の投稿

C# 外部ライブラリを使わずに半角→全角カタカナ変換

C# WebView2.ExecuteScriptAsync()のいくつかの使い方とDebug方法

WebView2 Runtimeが見つからないエラー 解決法一例(凡ミスだった)