こんにちはー!ニアです。
今回はWPFのChartコントロールでデータバインディングする時に、気を付けることについてお話しします。
今回使用したChartコントロールは以下の2つです。
1. 問題のコード
例えば、Logistic写像「x(n + 1) = ax(n)( 1 – x(n) )」のグラフを描画するプログラムを作成するとします。
<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のXPathとYPath及び、IndependentValuePathとDependentValuePathにはプロパティ名を指定する必要があります。
従って、PointクラスのXとX_Nをフィールドでなく、プロパティで定義します。
// 点
public class Point {
public int N { get; set; }
public double X_N { get; set; }
}
これで、Logistic写像を無事に表示することができます。
3. エラーが起きても、慌てずに落ち着いて!
クラスを利用する側では、メンバーの呼び出す構文がフィールドとプロパティで同じです。
かつて私は、アプリでChartコントロールを使用した時に、XY座標に指定しているメンバーがフィールドになっていたことに気付くのに苦労したことがあります。
Visual Studioでは、クラスなどのメンバーにカーソルを当てると現れるツールチップで、フィールドとプロパティを簡単に判別することができます。
フィールドの場合、メンバーの前に「(フィールド)」が付いており、プロパティの場合、getとsetの情報が付いています。
エラーが起きたら、慌てず落ち着いて対処しましょう。
WPFのクラスで外部に公開するメンバーは、基本的にフィールドではなくプロパティを使うのがよいですね。(PropertyChengedなどのイベントハンドラは例外です。)
[END]
コメント