WPFでグラフを作成するにはどうすればいいの?
WPF にはグラフを作成するコントロールが標準で用意されていません。そのため、グラフを作成するには次に挙げている方法で実装する必要があります。
- Nuget からグラフ作成用のライブラリをインストールして使用する
- .NET Framework に含まれている Chart コントロールを使用する
この記事では、①のNuget からグラフ作成用のライブラリをインストールしてグラフを作成する方法を紹介します。
無料で使える有名なライブラリとして、LiveCharts・OxyPlot・ScottPlotの3種類があります。
今回はその3種類の中からアニメーションが豊富でビジュアルに特化した「Live Charts」を使って、WPF にグラフを作成する方法をご紹介します。
Live Charts とは
Live Charts は、.NET 向けのグラフ作成用のライブラリーです。そのため WPF および Windows Forms 、Core に対応しています。(2022年5月現在)
特徴
特徴としてグラフを描画した時などにアニメーション処理が施されており、とても見やすくデータを可視化してくれます。また、マウスカーソルをグラフの上に移動させると、グラフのプロット情報がカーソルの近くに表示されます。Material Designもアニメーションが豊富なので相性が良さそうです。
下図は LiveCharts を使って実際に作成したグラフです。
マウスホバーした時にプロット情報を ToolTips で表示します。この ToolTips は好きなように表示内容をカスタマイズすることが可能です。
興味ある方は次の記事でカスタマイズする方法を紹介していますので、参考にしてみてください。
グラフの種類
さまざまなグラフを作成できます。普段よく使われる折れ線グラフや縦棒グラフをはじめ、散布図や円グラフなど多彩なグラフを作成することができるのでとても便利ですね。
下の図は、公式ホームページにサンプルで載っているグラフの一部です。グラフの種類が多いので、どんなニーズにも対応できそうです。
公式ホームページの『Tutorial and samples』にグラフ毎のサンプルソースが記載されています。サンプルも充実していますね。
欠点
グラフで表示するデータ件数が1万件を超えると、他のチャートコントロールと比べると描画速度が速度が遅くなってしまうというデメリットがあります。
アニメーション処理を無しにすることで多少マシになるかもしれませんが、限界があるでしょう。
この問題に関しては、有料版を購入することで解決できます。ライセンスは約7,000円です。
有料版を購入することで『High performance series』が解禁されるので描画速度が速くなるようです。大量のデータ件数を扱う場合に、グラフの表示時間にストレスを感じたら、無料版から有料版に切り替えることを検討するといいでしょう。
Live Chartsのインストール
この記事では次の開発環境でプログラムを作成します。
- OS:Windows 10 Home
- 開発環境:Visual Studio 2019
- 言語:C#
- 開発プラットフォーム:WPF
Live Charts のインストール手順は次の通りです。
ソリューションエクスプローラーを開いて、プロジェクト名の上にカーソルを移動させて右クリックします。Nugetの管理画面を開いて検索欄に『LiveCharts』を入力します。
検索結果の一覧から『LiveCharts.Wpf』を選択して、インストールを行います。関連パッケージである『LiveCharts』も同時にインストールされます。最新の安定版は2017年6月20日公開されている 0.9.7 でした。
プログラムの実装
ここでは以前紹介したメニュー画面にユーザーコントロールを追加して、グラフを作成します。
次の記事を参考にして「UserControlChart」という名前でユーザーコントロールを追加しておきましょう。
※この手順を踏まなくても、MainWindow に後述するソースコードを記載すればグラフは作成できます。
さっそく XAML とコードをそれぞれ記述します。
XAML実装
XAMLファイルを開いて、ファイルのヘッダー部分に LiveCharts 参照先を指定する1行を追加します。
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
次にコントロールを定義します。
SeriesCollection クラスや AxisX クラスなどの各種プロパティに対して Binding をするコードを実装します。
また、X 軸のラベルの感覚が狭いと、全てのラベルが表示されずに歯抜けになってしまいます。グラフの幅が相当広くないと全てのX軸のラベルが表示されません。
この問題を解決する方法として、Axis クラスの Seqarator プロパティの Step を”1”にします。
こうすることで、全ての X 軸ラベルを表示することができます。注意点としてグラフの幅が小さすぎると、軸ラベルが重なってしまいますので、充分な感覚を取る必要があります。
<lvc:CartesianChart
x:Name="lvChart"
Series="{Binding seriesCollection}">
<lvc:CartesianChart.AxisX>
<lvc:Axis Labels="{Binding labels}">
<lvc:Axis.Separator>
<lvc:Separator IsEnabled="False" Step="1" />
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis MaxValue="10.0" MinValue="0.0" />
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
UserControlChart の UI 画面にグラフ更新ボタンやテキストを配置します。コントロールを追加し、位置調整をした XAML が以下になります。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<materialDesign:PackIcon
Width="35"
Height="35"
Margin="10"
VerticalAlignment="Center"
Foreground="{StaticResource PrimaryHueMidBrush}"
Kind="ChartBar" />
<TextBlock
Margin="0,0,40,0"
HorizontalAlignment="Center"
Style="{StaticResource MaterialDesignHeadline4TextBlock}"
Text="Chart" />
<Button
x:Name="btnUpdate"
Width="100"
Margin="10,9"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="btnUpdate_Click"
Style="{StaticResource MaterialDesignOutlinedButton}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Update" />
<TextBlock Text="Update" />
</StackPanel>
</Button>
</StackPanel>
<lvc:CartesianChart
x:Name="lvChart"
Grid.Row="1"
Margin="10,10"
Series="{Binding seriesCollection}">
<lvc:CartesianChart.AxisX>
<lvc:Axis Labels="{Binding labels}">
<lvc:Axis.Separator>
<lvc:Separator IsEnabled="False" Step="1" />
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis MaxValue="10.0" MinValue="0.0" />
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
</Grid>
コードの実装
UserControlChart のコードビハインドの先頭に LiveCharts の名前空間を参照する為のコードを記述します。
using LiveCharts;
using LiveCharts.Wpf;
グラフのプロパティが変更されたことを View に通知するために INotifyPropertyChanged を使った ViewModel という名前のクラスを新たに作成します。
ViewModel クラスは、INotifyPropertyChanged を継承します。このクラスをつくることで、SeriesCollection と AxisX の各種プロパティに値のセットと画面の反映を行うことができるようになります。
public class ViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged を実装するためのイベントハンドラ
public event PropertyChangedEventHandler PropertyChanged;
// プロパティ名によって自動的にセットされる
private void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
SeriesCollection _seriesCollectiont;
public SeriesCollection seriesCollection
{
get { return _seriesCollectiont; }
// 値をセットした後、画面側でも値が反映されるように通知する
set { if (_seriesCollectiont != value) { _seriesCollectiont = value; RaisePropertyChanged(); } }
}
List<string> _labels;
public List<string> labels
{
get { return _labels; }
// 値をセットした後、画面側でも値が反映されるように通知する
set { if (_labels != value) { _labels = value; RaisePropertyChanged(); } }
}
}
LiveCharts で利用できるグラフの種類は以下の通りです。
クラス | の種類 | グラフ
ColumnSeries | 縦棒グラフ |
RowSeries | 横棒グラフ |
StackedColumnSeries | 積み上げ縦棒グラフ |
StackedRowSeries | 積み上げ横棒グラフ |
LineSeries | 折れ線グラフ |
PieSeries | 円グラフ |
StackedAreaSeries | 面グラフ |
ColumnSeries クラスを使って縦棒グラフを描画する独自メソッドを作成します。引数を2つにして、第一引数に X 軸ラベルの項目の配列を、第二引数に描画データの配列を渡すとグラフを描画するようになっています。
//グラフを描く関数
private void DarwChart(string[] labels, double[] values)
{
//X軸のラベル
vm.labels = new List<string>(labels);
//グラフの値
vm.seriesCollection = new SeriesCollection
{
new ColumnSeries
{
Values = new ChartValues<double>(values),
Fill = Brushes.DarkOrange
}
};
}
LiveCharts では複数の Series (データ系列) をこの SeriesCollection クラスで管理します。ColumnSeries クラスにグラフの値と色の設定パラメーターを渡します。
UserControlChart の初期処理時は、グラフの値は全て0にします。更新ボタンが押下されたらランダムで値を算出して、グラフに描画する値の配列を作成します。その配列を描画メソッドに渡してグラフの描画を行います。
vm.labels や vm.seriesCollection の値を更新すると、グラフも自動で再描画されます。
ソースコードは次の様になっています。
/// <summary>
/// UserControlChart.xaml の相互作用ロジック
/// </summary>
public partial class UserControlChart : UserControl
{
// ViewModel のインスタンス
ViewModel vm = new ViewModel();
public UserControlChart()
{
InitializeComponent();
DataContext = vm;
//グラフの描画
var labels = new string[] { "Apple", "Banana", "Grapes", "Kiwi", "Melon", "Orange" };
var values = new double[] { 0, 0, 0, 0, 0, 0 };
DarwChart(labels, values);
}
//グラフを描く関数
private void DarwChart(string[] labels, double[] values)
{
//X軸のラベル
vm.labels = new List<string>(labels);
//グラフの値
vm.seriesCollection = new SeriesCollection
{
new ColumnSeries
{
Values = new ChartValues<double>(values),
Fill = Brushes.DarkOrange
}
};
}
private void btnUpdate_Click(object sender, System.Windows.RoutedEventArgs e)
{
//ランダムでグラフの値をリスト化
var values = new double[6];
for (int i = 0; i < values.Length; i++)
{
var seed = Convert.ToInt32(Guid.NewGuid().ToString("N").Substring(0, 8), 16);
values[i] = new Random(seed).Next(1, 10);
}
//グラフの描画
var labels = new string[] { "Apple", "Banana", "Grapes", "Kiwi", "Melon", "Orange" };
DarwChart(labels, values);
}
}
アプリケーションの起動
さっそく、実装したプログラムをデバックしてみましょう。デバックしたイメージ画像がこちらです。
WPF でもグラフが描画できましたね。LiveCharts の縦棒グラフのアニメーションは下からグッと指定のパラメーターまで上昇します。
個人的には Material Desgin とマッチしていて、結構いい感じだと思っています。
アニメーションの制御方法
描画時のアニメーションを無くして、描画速度を少しでも速くしたいと思う方もいらっしゃるかもしれません。そのような場合は、以下の方法でアニメーションの ON / OFF を切り替えることができます。
方法は、WPF内にあるのタブの中に DisableAnimations プロパティを追加し、そのプロパティへ True を代入することで、アニメーション処理を OFF にすることができます。
DisableAnimations="True"
アニメーションの描画をOFFにした場合が以下です。用途によって使い分けをしましょう。
最後に
今回は LiveCharts で簡単に描画するための方法について解説しました。
グラフの描画件数が多い場合は遅延が発生してしまいますが、それ以外の不満はなく比較的扱いやすいライブラリだと感じました。
描画時の遅延が気になる方は、有料版を使うことで解決ができますので検討してみるといいでしょう。
個人的には、グラフの種類も十分ですし、操作した時のアニメーションもあり Material Design との相性がいい所も気に入っています。今後グラフ使う機会あれば、積極的に LiveCharts を使っていこうと思います。
WPF でグラフを書く際の参考になれば幸いです。
以上、最後まで読んで頂きありがとうございました。