WPFでChartグラフを表示するためのライブラリーに OxyPlot があります。OxyPlot には 散布図用に ScatterPoint があり、点ごとにサイズと色が指定できます。要素数制限付きの ObservableCollection と合わせて、リアルタイム表示用に残像的なイメージのアニメーションサンプルを作ってみました。
- OxyPlot アプリケーションの基本構成
- 要素数制限付きの ScatterPointCollection
- ScatterPointCollection と DispatcherTimer の追加
- View の作成
- LinerColorAxis の設定
- ScatterSeries の設定
- Plot v.s. PlotView & PlotModel
- 作成したソースコードの場所
OxyPlot の基本はこれを参考にしてください。
OxyPlot アプリケーションの基本構成
Chart は Blend もしくは VisualStudio の XAMLデザイナーで作成して、ViewModel の座標データとバインドする事により、point を追加すると Chart の更新が自動で行われる様にしました。

基本的な構成は次の様になっています。

Model
- 要素数制限付きの ScatterPointCollection クラスを定義
ViewModel
- ScatterPointCollection ScatterPoints を作成
- DispatcherTimer で一定時間ごとに ScatterPoint を追加する為の設定
- DispatcherTimer の起動/停止用の StartFlag プロパティーを設定
View
View にPlotを貼り付ける
Plot の PlotAreaBorderThickness を 0 にして枠線を消す
Plot の PlotType を Polar にするPlot の Axes に 軸を追加
MagnitudeAxis:PolarChartの中心からの距離軸
AngleAxis:PolarChartの角度軸
LinearColorAxis:ScatterPoint の Value の値に応じて色を付ける為の軸Plot の Series に ScatterSeries を追加
ScatterSeries の ItemSource に ViewModel の ScatterPoints をバインドView に Checkbox を貼り付けて、ViewModel の StartFlag とバインド
要素数制限付きの ScatterPointCollection
ObservableCollection を継承して ScatterPoint 用の ScatterPointCollection を作成します。
ScatterPointCollection に
要素数の上限用のプロパティ Limit と Size
上限と下限用に SizeMax, SizeMin,
Value の上限と下限用に ValueMax, ValueMin
を追加します。
Size と Value は、要素の順番で変化させます。
public class ScatterPointCollection : ObservableCollection<ScatterPoint>
{
protected bool SetProperty<T>(ref T field, T value,
[CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
return true;
}
protected virtual void Shrink(int index)
{
while (Limit < Count) RemoveAt(0);
for(int i = 0;i < Count; i++)
{
var item = Items[i];
item.Size = (SizeMax - SizeMin) * (i + 1) / Count + SizeMin;
item.Value = (ValueMax-ValueMin)*(i+1)/(double)Count+ValueMin;
Items[i] = item;
}
}
private double sizeMax = 1.0;
public double SizeMax
{
get { return sizeMax; }
set { SetProperty(ref sizeMax, value); }
}
private double sizeMin = 1.0;
public double SizeMin
{
get { return sizeMin; }
set { SetProperty(ref sizeMin, value); }
}
private double valueMax = 1.0;
public double ValueMax
{
get { return valueMax; }
set { SetProperty(ref valueMax, value); }
}
private double valueMin = 0.0;
public double ValueMin
{
get { return valueMin; }
set { SetProperty(ref valueMin, value); }
}
private int limit = 50;
public int Limit
{
get { return limit; }
set
{
if (value < 1) value = 1;
if (SetProperty(ref limit, value))
{
Shrink(Count);
}
}
}
protected override void InsertItem(int index, ScatterPoint item)
{
base.InsertItem(index, item);
Shrink(index);
}
}
ScatterPointCollection と DispatcherTimer の追加
ViewModel に ScatterPoints を追加します。
ScatterPoints は ObservableCollection を継承しているので、UIスレッド以外からは操作できません。
なので、Timer は DispatcherTimer を使用してUIスレッドからPointを追加するようにします。
public MainWindowViewModel()
{
ScatterPoints.Limit = 50;
ScatterPoints.SizeMax = 5.0;
ScatterPoints.SizeMin = 1.0;
timer.Interval = TimeSpan.FromMilliseconds(25) ;
timer.Tick += new EventHandler(MovingPoint);
}
private DispatcherTimer timer = new DispatcherTimer();
private ScatterPointCollection scatterPoints = new ScatterPointCollection();
public ScatterPointCollection ScatterPoints
{
get { return scatterPoints; }
set { SetProperty(ref scatterPoints, value); }
}
private double dA1 = 10.0;
private double dA2 = 0.7 / 180.0 * Math.PI;
private double ang1 = 0.0;
private double ang2 = 0.0;
/// <summary>
/// DispatcherTimerで実行するポイント追加メソッド
/// UIスレッドで実行される
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MovingPoint(object sender, EventArgs e)
{
double mag = 50.0 + 40.0 * Math.Sin(ang2);
double ang = ang1;
var p = new ScatterPoint(mag, ang);
ScatterPoints.Add(p);
ang1 += dA1;
ang2 += dA2;
}
StartFlag で DispatcherTimer の On/Off を切り替えます。
DispatcherTimer が起動すると メソッド MovingPoint によって ScatterPoint が追加されます。
private bool startFlag = false;
/// <summary>
/// DispatcherTimerのStart/Stop
/// </summary>
public bool StartFlag
{
get { return startFlag; }
set {
if (value == true) timer.Start();
else timer.Stop();
SetProperty(ref startFlag, value);
}
}
View の作成
Plot を貼り付け PlotType を Polar にします。
PlotAreaBorderThickness を 0 にして枠線を消しておきます。
次に OxyPlot の プロパティの Axes をクリックして軸を追加します。
OxyPlot の軸には次のようなものがあります。

この中から MagnitudeAxis と AngleAxis と LinearColorAxis を追加します。

それぞれの軸のMaximum, Minimum、Gridの設定を行います。
LinerColorAxis の設定
LinerColorAxis は ScatterPoint の Value の値に応じて色を付けます。
色は LinearColorAxis の GradientStops の設定で行います。
プロパティの GradientStops をクリックし GradientStop を追加します。
GradientStop は 0-100%の間で3点以上必要みたいです。
色の階調は PalletSize で指定します。

ScatterSeries の設定
Plot の Series に ScatterSeries を追加し、ItemSource に ViewModel の ScatterPoints をバインドします。MarkerType を Circle にします。


あとは、View に Checkbox を貼り付けて、ViewModel の StartFlag で DispacherTimer の起動と停止ができるようにすると、サンプルアプリの完成です。
Plot v.s. PlotView & PlotModel
OxyPlot を使用する場合、View に Plot を貼りつけて XAML で Chart を作成して ViewModel のプロパティとバインドする方法と、View に PlotView を貼りつけて表示用の器だけ用意し、ViewModel にPlotModel を作り、コードで Chart を作成する方法の2通りがありますが、その特徴をまとめておきます。
【ViewにPlotを貼りつける場合】
- Chart 本体は View 側の Plot
- 軸、表示する線の定義を XAML で行う
XAMLエディターのプロパティーを修正しながらリアルタイムで Chart が確認できるので初心者向き。動的に線を増やしたりできないので、予め多めに作って IsVisible で隠したりする必要がある。 - ViewModel の点座標用のプロパティーを Plot の Series にバインドする
- Chartの更新はプロパティーの変更通知でできる ObservableCollection を使用すると、点を追加するたびに自動更新される
【View に PlotView を貼りつける場合】
- Chart 本体は ViewModel 側の PlotModel
- 軸とか表示する線の定義は ViewModel のコードで行う。 慣れている人はコードの方が XAML より簡単にかけるが、確認のたびにビルドしないといけない。コードで記述するので、動的に線を増やしたりできる。
- ViewModel の plotModel を PlotView にバインドする
- Chartの更新は PlotModel の InvalidatePlot(true) で行う
作成したソースコードの場所
ソースコードは次の場所に置いてあります。
*参考 - View から ViewModel にBlendのダイアログで選択してバインドするためには、View のデザイン時の DataContext にViewModel が設定されていることが必要です。 (メニューの [形式]-[デザイン時のDataContextの設定] から設定できます)