Visual Studio | WPF > OxyPlot > 折れ線グラフ描画のパフォーマンス > 1万点: 瞬時に描画 | zooming and panning: デフォルトで有効 | 1秒ごとにデータ追加の実装例


動作環境
Windows 8.1 Pro (64bit)
Microsoft Visual Studio 2017 Community
OxyPlot.Wpf v1.0.0
Sublime Text 2

@yumetodo さんに推薦いただいたOxyPlotを試してみた。

関連

参考

WPF OxyPlotで円グラフを作る by @kuro4 さん

情報感謝です。

ただし、MVVMなど入れると複雑になるため、簡易的な実装とする。

準備

  • NugetでOxyPlot.Wpf v1.0.0をインストール
  • XAMLに以下を追加
    • xmlns:oxy="http://oxyplot.org/wpf"
  • code behindに以下を追加
    • using OxyPlot;
    • using OxyPlot.Series;

code

MainWindow.xaml
<Window x:Class="_171127_t1530_tooManyPoints.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_171127_t1530_tooManyPoints"
        xmlns:oxy="http://oxyplot.org/wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <oxy:PlotView Model="{Binding _PlotModel, Mode=OneWay}"/>
    </Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// 以下を追加した
using OxyPlot;
using OxyPlot.Series;

namespace _171127_t1530_tooManyPoints
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public PlotModel _PlotModel { get; private set; } = new PlotModel() { Title = "LineSeries" };

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

        private void graph_init()
        {
            var series = new LineSeries
            {
                StrokeThickness = 0.5,
            };

            graph_add_series_addEach(series);
            _PlotModel.Series.Add(series);

            this.DataContext = this;
        }

        static readonly int kNumPoint = 10000;

        private void graph_add_series_addEach(LineSeries series)
        {
            var gen = new Random();
            for (int idx=0; idx < kNumPoint; idx++)
            {
                var yval = gen.NextDouble() * 10.0;
                series.Points.Add(new DataPoint(idx, yval));
            }            
        }
    }
}

  • 1万点: 瞬時に描画
  • 10万点: 3秒程度の後に描画

zooming and panning

デフォルトで有効になっていた。

操作についてはこちら

パフォーマンスも良い。

1秒ごとにデータ追加の実装例

参考: OxyPlotのコントロール(PlotView)をリアルタイムにアップデートする方法

上記にて表示の更新方法を知りました。
InvalidatePlot()を使用する方法を使ってみます。

情報感謝です。

MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
// 以下を追加した
using OxyPlot;
using OxyPlot.Series;
using System.Windows.Threading;

namespace _171127_t1530_tooManyPoints
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer myTimer;
        private LineSeries mySeries;
        private Random randGen;
        private int pos = 0;
        public PlotModel _PlotModel { get; private set; } = new PlotModel() { Title = "LineSeries" };

        public MainWindow()
        {
            InitializeComponent();

            // 1. 1秒タイマーの設定
            myTimer = new DispatcherTimer(DispatcherPriority.Normal);
            myTimer.Interval = new TimeSpan(0, 0, 1);
            myTimer.Tick += myTimer_Tick;

            randGen = new Random();

            graph_init();

            myTimer.Start();
        }

        private void graph_init()
        {
            mySeries = new LineSeries
            {
                StrokeThickness = 0.5,
            };

            graph_add_series_addEach();
            _PlotModel.Series.Add(mySeries);
            this.DataContext = this;
        }

        static readonly int kNumAddPoint = 5000;
        static readonly int kMaxPoint = 200000;

        private void graph_add_series_addEach()
        {
            for (int idx=0; idx < kNumAddPoint; idx++)
            {
                var yval = randGen.NextDouble() * 10.0;
                mySeries.Points.Add(new DataPoint(pos, yval));
                pos++;
            }
        }

        void myTimer_Tick(object sender, EventArgs e)
        {
            if (pos > kMaxPoint)
            {
                return;
            }

            graph_add_series_addEach();
            _PlotModel.InvalidatePlot(true);
        }
    }
}
  • 1秒ごとにデータを追加する
  • 20万点でデータ追加を停止する

20万点ではpanningが重いが、描画はできている。

TeeChartなみのことはできそうだ。