[WPF] Chartコントロールのデータバインディングで気を付けたい点

こんにちはー!ニアです。

今回はWPFのChartコントロールでデータバインディングする時に、気を付けることについてお話しします。

今回使用したChartコントロールは以下の2つです。

1. 問題のコード

例えば、Logistic写像「x(n + 1) = ax(n)( 1 – x(n) )」のグラフを描画するプログラムを作成するとします。

chart1
<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
		xmlns:wtk = "clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
		Title="Logistic写像" Height="480" Width="840">
	<Window.DataContext>
		<local:Logistic/>
	</Window.DataContext>
	<wtk:Chart>
		<wtk:Chart.Axes>
			<wtk:LinearAxis Orientation="X"
							Minimum="0" Maximum="100"
							Title="n"/>
			<wtk:LinearAxis Orientation="Y"
							Minimum="0" Maximum="1"
							Title="x(n)"/>
		</wtk:Chart.Axes>
		<wtk:LineSeries Title="a=4.0, x(0)=0.1"
						ItemsSource="{Binding Attractor}"
						IndependentValuePath="N"
						DependentValuePath="X_N"/>
	</wtk:Chart>
</Window>
<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        xmlns:sc="http://sparrowtoolkit.codeplex.com/wpf"
		Title="Logistic写像" Height="480" Width="840">
	<Window.DataContext>
		<local:Logistic/>
	</Window.DataContext>
	<sc:SparrowChart Margin="10">
		<sc:SparrowChart.Legend>
			<sc:Legend Dock="Right" Margin="10"/>
		</sc:SparrowChart.Legend>
		<sc:SparrowChart.XAxis>
			<sc:LinearXAxis MinValue="0" MaxValue="100"
							Interval="10" Header="n"/>
		</sc:SparrowChart.XAxis>
		<sc:SparrowChart.YAxis>
			<sc:LinearYAxis MinValue="0" MaxValue="1"
							Interval="0.2" Header="x(n)"/>
		</sc:SparrowChart.YAxis>
		<sc:LineSeries Label="a=4.0, x(0)=0.1"
					   PointsSource="{Binding Attractor}"
					   XPath="N" YPath="X_N"/>
	</sc:SparrowChart>
</Window>
using System.Windows;
using System.Collections.ObjectModel;

namespace WpfApplication2 {

	// 点
	public class Point {
		public int N;
		public double X_N;
	}

	public class Logistic {
		// アトラクター
		public ObservableCollection<Point> Attractor { get; set; }

		public Logistic() {
			Attractor = new ObservableCollection<Point>() {
				new Point { N = 0, X_N = 0.1 }
			};
			for( int n = 1; n <= 100; n++ ) {
				Attractor.Add( new Point { N = n, X_N = Map( 4.0, Attractor[n - 1].X_N ) } );
			}
		}

		// Logistic写像
		double Map( double a, double x ) =>
			a * x * ( 1.0 - x );
	}

	public partial class MainWindow : Window {

		public MainWindow() {
			InitializeComponent();
		}
	}
}

しかし、このコードを実行すると、Sparrow Chart側ではNullReferenceExceptionが、WPF Toolkit側ではInvalidOperationExceptionがスローされます。

2. 解決方法

どちらのChartコントロールも例外がスローされたのは、X座標・Y座標として使用しているPointクラスのNとX_Nがプロパティでなく、フィールドとして定義されていたのが原因です。

LineSeriesのXPathYPath及び、IndependentValuePathDependentValuePathにはプロパティ名を指定する必要があります。

従って、PointクラスのXとX_Nをフィールドでなく、プロパティで定義します。

// 点
public class Point {
	public int N { get; set; }
	public double X_N { get; set; }
}

これで、Logistic写像を無事に表示することができます。

chart2

3. エラーが起きても、慌てずに落ち着いて!

クラスを利用する側では、メンバーの呼び出す構文がフィールドとプロパティで同じです。

かつて私は、アプリでChartコントロールを使用した時に、XY座標に指定しているメンバーがフィールドになっていたことに気付くのに苦労したことがあります。

Visual Studioでは、クラスなどのメンバーにカーソルを当てると現れるツールチップで、フィールドとプロパティを簡単に判別することができます。

chart3

フィールドの場合、メンバーの前に「(フィールド)」が付いており、プロパティの場合、getとsetの情報が付いています。

エラーが起きたら、慌てず落ち着いて対処しましょう。

WPFのクラスで外部に公開するメンバーは、基本的にフィールドではなくプロパティを使うのがよいですね。(PropertyChengedなどのイベントハンドラは例外です。)

[END]

コメント

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