時間の経過とともに変化するデータをグラフで表現してみませんか? InfragisticsのXamDataChartを使用すれば、そのようなグラフも簡単に作成することができます。 トライアル版のNuGetパッケージを使用しますので、Infragistics製品の事前インストールは必要ありません。 必要なものはVisualStudio2022だけです。さあ、始めましょう。
Visual StudioでWPFアプリケーションを新規作成する
Visual Studio 2022を開き、「新しいプロジェクトの作成」を選択します。
テンプレートは「WPFアプリケーション」を選択し、「次へ」をクリックします。
適当なプロジェクト名を付け、「次へ」をクリックします。
フレームワークは「.NET6(長期的なサポート)」を選択して「作成」をクリックします。
NuGetパッケージをインストールする
プロジェクトの生成が完了したら、XamDataChartを使用するためのNuGetパッケージをインストールします。 Visual Studioの上部のメニューから「ツール」>「NuGetパッケージマネージャー」>「ソリューションのNuGetパッケージの管理」を選択します。
NuGetパッケージの管理画面で、以下のスクリーンショットのように、 ① パッケージソースに「nuget.org」を選択、② 「参照」タブをクリック、③ 検索ボックスに「Infragistics.WPF.Charts」と入力、④ 表示された「Infragistics.WPF.Charts.Trial」をクリックして選択、⑤ 現在のプロジェクトをチェック、⑥ 「インストール」ボタンをクリックします。
変更のプレビュー画面が表示されたら、「OK」ボタンをクリックします。
以上の操作により、NuGetパッケージInfragistics.WPF.Charts.Trialがプロジェクトにインストールされ、XamDataChartを使用するための準備が整いました。
データの定義
インストールが完了したら、「NuGet – ソリューション」のタブを閉じ、MainWindow.xaml.csを開き、コードビハインドでグラフにバインドするデータを作成します。 まずはデータポイントを表すMyDataPointクラスの定義です。時間軸であるX軸を表すDateTimeタイプのMyDateプロパティと、数値軸であるY軸を表すdoubleタイプのMyValueプロパティを追加しました。
public class MyDataPoint { public DateTime MyDate { get; set; } public double MyValue { get; set; } }
次に、グラフにバインドするためのデータコレクションを保持するChartDataSourceクラスを用意します。こちらはMyDataPoint タイプのObservableCollectionとなっています。
public class ChartDataSource : ObservableCollection<MyDataPoint> { private Random _rand = new Random(); private float _baseVal = 50; DateTime dt = DateTime.Today; public ChartDataSource() { addDataItem(); } public void addDataItem() { if (_rand.NextDouble() > .5) { _baseVal += _rand.Next(0, 5); if (_baseVal > 100) { _baseVal = 100; } } else { _baseVal -= _rand.Next(0, 5); if (_baseVal < 0) { _baseVal = 0; } } this.Add(new MyDataPoint { MyDate = dt, MyValue = _baseVal }); dt = dt.AddSeconds(5); } }
ChartDataSourceクラスは自身のコレクションにMyDataPointインスタンスを追加するaddDataItem()メソッドを持ちます。 MyDataPointインスタンスは生成時にMyDate プロパティにはDateTime.Todayを起点として5秒刻みの日付値を、MyValueプロパティには都度ランダムの数値を生成して割り当てます。
MainWindowのコンストラクタでChartDataSourceをインスタンス化し、MainWindowのDataContextに割り当てます。
private ChartDataSource _data; public MainWindow() { InitializeComponent(); _data = new ChartDataSource(); this.DataContext = _data; }
以上でデータクラスの準備は整いました。
XamDataChartを配置する
次に、MainWindow.xamlを開いてXamDataChartのコーディングを始めます。 まず、Infragisticsのコントロールを参照するためのネームスペースをWindowに追加します。
<Window ….. xmlns:ig="http://schemas.infragistics.com/xaml"> ….. </Window>
次に、WindowにXamDataChartを配置していきます。
<Window…..> <Grid> <ig:XamDataChart x:Name="xamDataChart1" Margin="25"> </ig:XamDataChart> </Grid> </Window>
ここでは軸ラベルの見切れを回避するため、Marginプロパティのみ"25"に設定しています。
次に、軸の定義を行います。Y軸は数値軸となるためNumericYAxisを、X軸は時間軸となるためTimeXAxisを定義します。それぞれの定義をXamDataChart.Axesコレクションに追加します。
<ig:XamDataChart.Axes> <ig:TimeXAxis x:Name="xAxis" ItemsSource="{Binding}" DateTimeMemberPath="MyDate" > <ig:TimeXAxis.Intervals> <ig:TimeAxisInterval Range="0.00:30:00" Interval="10" IntervalType="Minutes" /> </ig:TimeXAxis.Intervals> <ig:TimeXAxis.LabelFormats> <ig:TimeAxisLabelFormat Format="HH:mm:ss" Range="0.00:00:00"/> </ig:TimeXAxis.LabelFormats> </ig:TimeXAxis> <ig:NumericYAxis x:Name="yAxis" MinimumValue="0" MaximumValue="100"/> </ig:XamDataChart.Axes>
TimeXAxisの実装のポイントはItemsSourceプロパティにDataContextをバインドしていること、そしてDateTimeMemberPathプロパティに先ほどMyDataPointクラスで定義したMyDateプロパティを指定していることです。また、IntervalsおよびLabelFormatsコレクションには今回のグラフで使用する範囲のみ、軸インターバルとラベル書式設定を行っています。 NumericYAxisでは軸の最小値(MinimumValue)と最大値(MaximumValue)のみ指定しています。
次にXamDataChart.Seriesコレクションを定義します。今回のチャートは折れ線グラフのため、LineSeriesを使用します。LineSeriesのインスタンスをXamDataChart.Seriesコレクションに追加します。
<ig:XamDataChart.Series> <ig:LineSeries XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ValueMemberPath="Value" ItemsSource="{Binding}" MarkerType="None"/> </ig:XamDataChart.Series>
XAxisプロパティとYAxisプロパティにはそれぞれ上記で定義したTimeXAxis とNumericYAxisを指定しています。また、ItemsSource にはTimeXAxisの時と同様、DataContextをバインドし、ValueMemberPathにはMyDataPointクラスで定義したMyValueプロパティを指定しています。今回はマーカー表示は行いませんので、MarkerTypeプロパティはNoneとします。
XamDataChartの全体像は以下のようになります。
<ig:XamDataChart x:Name="xamDataChart1" Margin="25"> <ig:XamDataChart.Axes> <ig:TimeXAxis x:Name="xAxis" ItemsSource="{Binding}" DateTimeMemberPath="MyDate" > <ig:TimeXAxis.Intervals> <ig:TimeAxisInterval Range="0.00:30:00" Interval="10" IntervalType="Minutes" /> </ig:TimeXAxis.Intervals> <ig:TimeXAxis.LabelFormats> <ig:TimeAxisLabelFormat Format="HH:mm:ss" Range="0.00:00:00"/> </ig:TimeXAxis.LabelFormats> </ig:TimeXAxis> <ig:NumericYAxis x:Name="yAxis" MinimumValue="0" MaximumValue="100"/> </ig:XamDataChart.Axes> <ig:XamDataChart.Series> <ig:LineSeries XAxis="{Binding ElementName=xAxis}" YAxis="{Binding ElementName=yAxis}" ValueMemberPath="MyValue" ItemsSource="{Binding}" MarkerType="None"/> </ig:XamDataChart.Series> </ig:XamDataChart>
MainWindow.xamlの定義は以上です。
リアルタイムのデータフィードを処理する
ここで、リアルタイムでデータのフィードを行うためのDispatcherTimerの実装をコードビハインドに追加していきます。 WindowのLoadedイベントを以下のように実装します。1秒間隔でDispatcherTimerのTickイベントを発行する内容です。
private void Window_Loaded(object sender, RoutedEventArgs e) { DispatcherTimer timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(1000); timer.Tick += Timer_Tick; timer.Start(); }
そして、Tickイベントは以下のように実装します。
private void Timer_Tick(object? sender, EventArgs e) { for (int i = 0; i < 120; i++) { _data.addDataItem(); } if (_data.Count > 840) { xAxis.MinimumValue = xAxis.ActualMinimumValue.AddSeconds(600); xAxis.MaximumValue = xAxis.ActualMaximumValue.AddSeconds(600); for (int i = 0; i < 120; i++) { _data.RemoveAt(0); } } }
1秒間隔で実行されるTimer_Tickで、グラフのデータコレクションに120ずつデータポイントを追加します。データポイントは5秒刻みで追加していますので、120だと120×5(秒)=600(秒)で10分間分のデータとなりますね。データポイントの総数が840を超えた場合(840×5(秒)=4200(秒)で、70分ぶんのデータポイントがたまった場合)、TimeXAxisのMinimumValueおよび MaximumValue のそれぞれの現在の値に600秒を追加し、グラフが左に流れるようにします。 また、その際にデータポイントを古い方から120削除します。
最後になりましたが、初期ロード時にTimeXAxisの表示範囲が60分となるよう、MaximumValueプロパティにDateTime.Today.AddMinutes(60) を設定します。 これはMainWindowのコンストラクタに追加すればよいでしょう。
public MainWindow() { InitializeComponent(); ….. xAxis.MaximumValue = DateTime.Today.AddMinutes(60); }
実装は以上です。
本記事のサンプルはこちらからダウンロードいただけます。