Karakuri.com

Fintechではたらくアプリケーションエンジニアの技術録

WPFアプリケーションでグラフチャートを導入する方法(WindowsFormHostとSparrow Toolkit)

WindowFormにはグラフ描画ライブラリが標準で用意されているのですが、WPFアプリケーションにはそのようなライブラリは用意されていません。今回はWindowsFormのチャートコントロールと、ググって見つけたSparrow Toolkitというチャートライブラリを使用してグラフ描画を行ってみました。

Windows Formのチャートコントロールを使う

WPFではWindowsFormHostを使ってWPF内でWindows Formのチャートコントロールを使ってグラフ描画を実装することができます。
qiita.com

しかし、これには問題があります。WindowsFormは全てのWPFコントロールの上面に表示されてしまうのです。このため、リッチなUIをデザインしたとき、本来は別のコントロールの下面になって隠れるはずの場合でも、上面に表示されてしまいます。つまりデザインが崩れるのです。
d.hatena.ne.jp

WPF4.5から修正されているとかされてないとか、強引に修正しても仕様変更で機能しなくなるとか、明確な回避方法が見当たりませんでした。更に、結局Windows Formのコントロールですので、パフォーマンスの問題も気になります。本当にちょっとしたグラフを描画するだけなら良いのですが、仕事で使うのは躊躇してしまいますね(そもそも製品に使うならサードパーティー製のライブラリ買った方がいいか…)。

Sparrow Toolkitのチャートコントロールを使う

WPFのチャートコントロールライブラリは幾つかあります。しかし、サポートが止まっていたり、利用者が全然いなくて使い方が分からないなど問題が多い場合がほとんどです。そんな中、Sparrow Toolkitは情報もそこそこあり、使えそうな雰囲気があったので試しに使ってみました。
sparrowtoolkit.codeplex.com

SparrowChartのインストール

僕はNugetを使ってプロジェクトにインストールしました。Visual StudioにNugetパッケージマネージャーをインストールし、パッケージマネージャーコンソールからコマンドを叩きました。

PM> Install-Package Sparrow.Chart.Wpf

Nugetについてはググればいくらでも出てくるので、そちらを参照してください。
yohshiy.blog.fc2.com

XAMLの書き方

インストールしたら早速XAMLでSparrowChartを表示してみましょう。

<Window x:Class="SparrowChart.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:SparrowChart"
        xmlns:sparrow="http://sparrowtoolkit.codeplex.com/wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="539.767" Width="535.234">
    <Grid>
        <sparrow:SparrowChart Visibility="Visible" VerticalAlignment="Top">
            <sparrow:SparrowChart.XAxis>
                <sparrow:LinearXAxis MinValue="0" MaxValue="10" Visibility="Visible"/>
            </sparrow:SparrowChart.XAxis>
            <sparrow:SparrowChart.YAxis>
                <sparrow:LinearYAxis MinValue="0" MaxValue="20" Visibility="Visible"/>
            </sparrow:SparrowChart.YAxis>
            <sparrow:LineSeries PointsSource="{Binding Path=Points}" XPath="X" YPath="Y"/>
            <sparrow:LineSeries PointsSource="{Binding Path=Points2}" XPath="X" YPath="Y" Stroke="Red" StrokeThickness="2"/>
        </sparrow:SparrowChart>
    </Grid>
</Window>

これで簡単な折れ線グラフを表示できます。縦軸と横軸が不要であってもSparrowChart.XAxisとYAxisは消さないでください。消してしまうとグラフが描画されません。表示したくない場合はVisibilityをHiddenやCollapsedにしましょう。

<sparrow:LineSeries PointsSource="{Binding Path=Points}" XPath="X" YPath="Y"/>
<sparrow:LineSeries PointsSource="{Binding Path=Points2}" XPath="X" YPath="Y" Stroke="Red" StrokeThickness="2"/>

ここが折れ線そのものの定義となります。折れ線グラフにはLineSeriesを使います。色は自動で割り当ててくれるので、指定せずとも同じ色になることはないのですが、Strokeプロパティで変更することもできます。他にもStrokeThicknessプロパティで線の太さを変えるなど、一般的なプロパティでグラフ描画を変更できるようになっています。

コードの書き方

さて、これだけでは当然グラフは描画されません。次はコードの書き方です。今回は表示についてだけなので、コードはMainWindow.xaml.csに直接書いてしまいます。

namespace SparrowChart
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<Point> Points { get; set; }
        public ObservableCollection<Point> Points2 { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            Points = new ObservableCollection<Point>();
            Points2 = new ObservableCollection<Point>();

            Points.Add(new Point { X = 0, Y = 0 });
            Points.Add(new Point { X = 1, Y = 5 });
            Points.Add(new Point { X = 2, Y = 7 });
            Points.Add(new Point { X = 3, Y = 14 });
            Points.Add(new Point { X = 4, Y = 10 });
            Points.Add(new Point { X = 5, Y = 12 });
            Points.Add(new Point { X = 6, Y = 7 });
            Points.Add(new Point { X = 7, Y = 18 });
            Points.Add(new Point { X = 8, Y = 18 });
            Points.Add(new Point { X = 9, Y = 10 });
            Points.Add(new Point { X = 10, Y = 14 });

            Points2.Add(new Point { X = 0, Y = 8 });
            Points2.Add(new Point { X = 2, Y = 3 });
            Points2.Add(new Point { X = 6, Y = 10 });
            Points2.Add(new Point { X = 10, Y = 5 });
            Points2.Add(new Point { X = 4, Y = 0 });

            this.DataContext = this;
        }
    }
}

XAMLのLineSeriesのPointSourceプロパティにPointとPoint2がバインディングされていました。このPointSourceプロパティにはObservableCollection型の変数をバインディングしてください。Point構造体にはXとYプロパティがありますが、これらはXAMLのXPathとYPathで指定されています。このため、Point構造体を使わなくてもXPathとYPathでプロパティを指定すればプロパティ名はXYである必要はありません。(あえて変えるメリットも分かりませんが)

実行結果

f:id:hazakurakeita:20160628010833p:plain

折れ線グラフが表示できました。

Points2.Add(new Point { X = 0, Y = 8 });
Points2.Add(new Point { X = 2, Y = 3 });
Points2.Add(new Point { X = 6, Y = 10 });
Points2.Add(new Point { X = 10, Y = 5 });
Points2.Add(new Point { X = 4, Y = 0 });

興味深いのは、XYを自動で並べ替えてくれるのではなく、Addした順に点が結ばれるということです。最後の点は(X=4, Y=0)ですが、(X=10, Y=5)の点から(X=4, Y=0)に線が結ばれています。感覚的には(X=2, Y=3)と(X=6, Y=10)の間に入りそうなので気をつけれければいけません。同じ要領で棒グラフなども描画できます。気が向いたらそっちのコードもいつか載せます笑。