IgbDataChart では、バインドした項目のプロパティに基づいて、マーカーの表示 (描画) をカスタマイズすることができます。
本稿では、このマーカー表示のカスタマイズ機能を用いて、下図のように、バインドした項目のプロパティに応じた大きさと塗りつぶし色のマーカーで散布図を表示する方法を説明します。
準備 – 説明のためのサンプルプログラムの作成
まずバインドするサンプルデータの型ですが、下記のレコード型とします。
この今回の説明用のレコード型ですが、一般的に散布図で使われるであろう、X軸・Y軸用の値として XValue、YValue といったプロパティを持つことに加えて、
– double 型の Volume プロパティと、
– System.Drawing.Color 型の Color プロパティ
を設けておきます。
この Volume プロパティ値をマーカーの大きさとして、Color プロパティ値をマーカーの塗りつぶし色として、マーカーの表示に用いることとします。
public record SampleDataType( string Name, double XValue, double YValue, double Volume, // ? このプロパティをマーカーの大きさに、 System.Drawing.Color Color // ? このプロパティを塗りつぶし色として参照 );
上記 SampleDataType レコード型を使用して、以下のとおり適当にサンプルデータを用意しておきます。
// サンプルの Razor コンポーネント (.razor) 中の @code ブロック内にて private IEnumerable<SampleDataType> _dataSource = [ new (Name: "item1", XValue: 0, YValue: 2, Volume: 5.7, Color: System.Drawing.Color.Fuchsia), new (Name: "item2", XValue: 2, YValue: 5, Volume: 3.6, Color: System.Drawing.Color.MediumOrchid), new (Name: "item3", XValue: 3, YValue: 1, Volume: 2.3, Color: System.Drawing.Color.DarkViolet), new (Name: "item4", XValue: 4, YValue: 6, Volume: 8.5, Color: System.Drawing.Color.SlateBlue), new (Name: "item5", XValue: 6, YValue: 3, Volume: 4.2, Color: System.Drawing.Color.RoyalBlue), ];
上記のとおり用意したサンプルデータを、別途用意した IgbDataChart 内に設けた、IgbScatterSeries のデータソースにバインドします (下記コード)。
@* サンプルの Razor コンポーネント (.razor) 内のマークアップにて *@ <IgbDataChart Height="320px" Width="320px"> <IgbNumericXAxis Name="xAxis" MinimumValue="0" MaximumValue="7" /> <IgbNumericYAxis Name="yAxis" MinimumValue="0" MaximumValue="10" /> <IgbScatterSeries DataSource="@_dataSource" XMemberPath="@nameof(SampleDataType.XValue)" YMemberPath="@nameof(SampleDataType.YValue)" XAxisName="xAxis" YAxisName="yAxis" /> </IgbDataChart>
ここまでの実装で、下図のとおり、散布図が表示されるようになります。
それではここから、この散布図におけるマーカーの表示を、冒頭の画像のように、バインドした項目のプロパティ (今回は Volume と Color) に応じた大きさと塗りつぶし色で表示するよう、カスタマイズしていきます。
本題 – マーカーの表示 (描画) のカスタマイズ
概要
バインドした項目のプロパティに基づいて、マーカーの表示 (描画) をカスタマイズするには、マーカーの描画を行なう JavaScript プログラムを作成し、その自作の JavaScript プログラムを使ってマーカーの描画を行なうようシリーズのプロパティに指定することで行ないます。
そのマーカーの描画を任される JavaScript プログラムには、シリーズから、HTML Canvas 要素の 2D コンテキストオブジェクトが渡されます。
その HTML Canvas 2D コンテキストに対して、自由に描画することでマーカーの描画を実装します。
以上のとおり、マーカーの表示 (描画) のカスタマイズのためには、JavaScript および HTML Canvas への描画についての事前知識が必要となります。
参考までに、HTML Canvas への描画に使用するキャンバス API についての情報ページへのリンクを下記に記します。
キャンバス API – Web API | MDN
1. マーカーのカスタム描画オブジェクトを返す、ファクトリー関数を定義
まずはじめに、マーカー描画の必要が発生する毎にIgnite UI 側から呼び出される measure および render という2つのメソッドを持つ JavaScript オブジェクトを返す、そのような関数を定義します。
例えば下記のとおり customMarkerTemplateFunc() とします。
// wwwroot/customMarkerTemplateFunc.js const customMarkerTemplateFunc = () => { return { measure: (mesureInfo) => { }, render: (renderInfo) => { } }; }
2. マーカーのカスタム描画オブジェクトの measure メソッドを実装
引き続き、measure メソッドを実装していきます。
この measure メソッドは、マーカーの大きさが必要となる度に、Ignite UI 側から呼び出されます。
そのとき、この measure メソッドの呼び出し時の引数には、その引数オブジェクトの .data.item フィールドに、描画対象のデータの JavaScript 側表現が含まれています。
つまり今回の例ですと、SampleDataType レコード型の各プロパティの値が、measure メソッド呼び出し時の引数から参照できますので、それに基づいてマーカーの大きさを算定し、Ignite UI からの呼び出しに対して回答とします。
Ignite UI への回答方法は、この measure メソッド呼び出し時の引数の width および height フィールドにマーカーの幅と高さ (いずれも px 単位) を設定することで行ないます。
以下にコード例を示します。
// wwwroot/customMarkerTemplateFunc.js const customMarkerTemplateFunc = () => { return { measure: (measureInfo) => { // この例では、描画するデータの Volume プロパティ値に基づいて、 // その 3倍を半径とした (なので直径はその2倍) 円をマーカーとして // 描画することとして、width と height を計算・設定しています。 const item = measureInfo.data.item; const size = item.Volume * 3 * 2; measureInfo.width = size; measureInfo.height = size; }, ...
3. マーカーのカスタム描画オブジェクトの render メソッドを実装
引き続き render メソッドを実装していきます。
この render メソッドは、マーカーの描画が必要となる度に、Ignite UI 側から呼び出されます。
そのとき、この render メソッドの呼び出し時の引数には、(measure メソッドと同じく) その引数オブジェクトの .data.item フィールドに、描画対象のデータの JavaScript 側表現が含まれています。
そのため、measure メソッドのときと同じように render メソッド呼び出し時も、その描画対象のデータを、その JavaScript 側表現を介して、マーカー描画のパラメータとして参照できます。
そしてまた、render メソッド呼び出し時の引数には、マーカーを描画する対象となる HTML Canvas 要素の 2D コンテキストオブジェクトも、そのフィールド変数に格納されています。
render メソッドでは、その引数経由で渡された 2D コンテキストオブジェクトに対して、マーカーの描画を実行します。
以下にコード例を示します。
// wwwroot/customMarkerTemplateFunc.js const customMarkerTemplateFunc = () => { return { ... render: (renderInfo) => { // 引数に渡された renderInfo に描画のための座標関係の情報がつまっているので、 // これを取り出しておく const { xPosition: cx, yPosition: cy } = renderInfo; const halfWidth = renderInfo.availableWidth / 2.0; const halfHeight = renderInfo.availableHeight / 2.0; // マーカーの塗りつぶしの色は、描画するデータの Color プロパティ値を使う // (ちなみに、既定のマーカーの塗りつぶし色は、 // renderInfo.data.actualItemBrush.fill に格納されています) const color = renderInfo.data.item.Color; // HTML Canvas 要素の 2D コンテキストに対して、マーカーの描画を実行 // (measure メソッドで算定した大きさで、真円のマーカーを描画) const ctx = renderInfo.context; ctx.beginPath(); ctx.fillStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; ctx.ellipse(cx, cy, halfWidth, halfHeight, 0, 0, 360 * Math.PI / 180); ctx.fill(); } ...
なお、HTML Canvas の 2D コンテキストに対して行える処理は何でも実行可能ですので、どのようなカスタム描画も実装できます。
ただし、マーカーを描画する全責任がこの render メソッドに任されていますから、マーカー表示に必要な処理は、些細なことでもすべてを自身で実装する必要があり、そのため HTML Canvas 要素への 2D 描画処理についての事前知識が必要となります。
4. マーカーのカスタム描画オブジェクトを返すファクトリー関数を、Ignite UI に登録
上記、measure および render メソッドが実装できたら、残りの作業はあと少しです。
こうして実装した、measure および render の 2つのメソッドを持つオブジェクトを返す関数を、Ignite UI が提供する igRegisterScript() 関数を呼び出して、Ignite UI に対して登録します。
このとき、igRegisterScript() 関数の第1引数に、文字列の “スクリプト名” を指定します。
このスクリプト名は任意の名称とすることができ、実装した JavaScript 関数の名前と同じである必要はありません。
Ignite UI からは、実際の JavaScript 関数の名前とは関係なく、この第1引数に指定した “スクリプト名” で識別されます。
以下にコード例を示します。
// wwwroot/customMarkerTemplateFunc.js const customMarkerTemplateFunc = () => { ... } // 上で実装したファクトリー関数を、Ignite UI に登録します。 // (※ファクトリー関数の JavaScript 上の名前とは関係なく、 // この登録時の第一引数で指定した "スクリプト" 名称で参照されます) igRegisterScript("customMarkerTemplateFunc", customMarkerTemplateFunc);
以上の JavaScript プログラムをブラウザに読み込ませるわけですが、ブラウザに読み込まれたときに、上記スクリプト登録が実行されつつグローバル汚染を避けるために、customMarkerTemplateFunc 関数の実装をそのまま igRegister 関数の引数内にインラインで記述してしまいます。
以下にコード例を示します。
// wwwroot/customMarkerTemplateFunc.js igRegisterScript("customMarkerTemplateFunc", () => { // ここに customMarkerTemplateFunc の実装内容を // インラインで記述 ... });
これで JavaScript 側の実装がひととおり完了となります。
こうして作成した JavaScript プログラムファイル (.js) を、ブラウザから読み込むように、フォールバックページ ( wwwroot/index.html や Pages/_Layout.cshtml、Pages/_Host.cshtml など) に script 要素を記載します。
その際、Ignite UI for Blazor の JavaScript ランタイムよりあとで読み込まれるよう、script 要素の配置順にはご注意ください。
以下に Blazor WebAssembly プログラムにおけるコード例を示します。
<!-- wwwroot/index.html --> ... <script src="_content/IgniteUI.Blazor/app.bundle.js"></script> <!-- Ignite UI for Blazor の JavaScript よりあとで、 マーカー表示のカスタム描画の JavaScript プログラムを読み込み --> <script src="customMarkerTemplateFunc.js"></script> ...
5. Razor コンポーネント側で、マーカー描画に指定のスクリプト名の JavaScript プログラムを使うよう指定
最後に、ここまでで作成した、マーカーのカスタム描画を行なう JavaScript を使うよう、シリーズのパラメータにて、”スクリプト名” で指定します。
具体的には MarkerTemplateScript という string 型のパラメータがありますので、ここに、マーカーのカスタム描画を行なう JavaScript プログラムのスクリプト名 (igRegisterScript() JavaScript 関数での登録時に、第1引数に指定した文字列で識別) を指定します。
下記にコード例を示します。
@* サンプルの Razor コンポーネント (.razor) 内のマークアップにて *@ <IgbDataChart Height="320px" Width="320px"> ... <IgbScatterSeries ... ... MarkerTemplateScript="customMarkerTemplateFunc" /> </IgbDataChart>
完成
これで、バインドした項目のプロパティに応じた大きさと塗りつぶし色のマーカーで、散布図が表示されるようになりました。
※補足: measure および render メソッド内で前後のデータを参照したい場合
MarkerTemplateScript に登録したスクリプト の measure および render メソッド内では、描画しようとしているデータだけでなく、前後のデータも .data.series.itemsSource で参照することができます。JavaScript の Array(n) 形式で格納されています。
measure: (measureInfo) => { ... // 描画しようとしているデータのシリーズに紐づいているデータ配列を // .data.series.itemsSource で参照できます。 measureInfo.data.series.itemsSource ... }, render: (renderInfo) => { ... // render メソッドも同様です。 renderInfo.data.series.itemsSource ... }
たとえば、折れ線グラフで欠損値を線形補間しない (UnknownValuePlotting.DontPlot)、かつ、マーカーを表示しない (MarkerType.None) という設定をした場合、欠損値に挟まれたデータ値が表示されなくなってしまいます。その場合だけはマーカーを描画したいといった場合に活用できると思います。
全体のサンプルコード
上記で説明したサンプルコード (.NET 8 上の Blazor WebAssembly で作成しました) の全てのファイルは、以下から Zip ファイルでダウンロード可能、あるいは GitHub リポジトリ上で参照可能です。
このサンプルコードでは、JavaScript 側実装は、TypeScript で記述しています。
コメントも多数付記しましたので、参考になればと思います。