LineGraph という名のカスタムコントロールを作ってみる その 2

今回はグラフの目盛線の描画方法を紹介しますが,
その前に基本となるクラスを説明しないことには,
途中で意味不明なコードが出てきますので,
まずはその基本クラスを簡単に紹介します.

紹介する基本クラスは NotifyPropertyChangedDataClass 抽象クラスです.
INotifyPropertyChanged インターフェースを実装するためのもので,
プロパティ値変更を容易に実装できるように工夫しています.

ポイントは SetProperty<T>() メソッドで,
このメソッドを使ってプロパティ値の変更をする/しないの判断を含めて処理させます.
実際の使用例を交えてグラフの軸設定に関するパラメータをまとめた
AxisStyle クラスを次のように定義します.

SetProperty() メソッドはプロパティ値に変更がある場合に true を返します.
幅 Width プロパティは最小値/最大値が変更されると同時に
変更通知をおこなわなければならないため,
最小値/最大値プロパティの set アクセサの中で変更通知をおこなうようにしています.

それぞれの set アクセサで,
value に対する条件を書いていますが,
実際には IDataErrorInfo インターフェースを実装して
入力値検証の仕組みも同時に実装させます.
ここでは省略します.

それでは
改めて LineGraph カスタムコントロールを次のように定義します.

上記のように Control クラスから派生させ,
静的なコンストラクタで LineGraph の既定スタイルを読み込むようにしておきます.

LineGraph に軸設定に関するパラメータを
依存関係プロパティとして定義します.
軸は横軸/縦軸/第 2 主軸の 3 種類あるので,
3 つ定義します.

宣言時に初期化をおこなっても構いませんが,
完成後の可読性を鑑みてコンストラクタ内にて初期化をおこないます.
プロパティの型は先ほど定義した AxisStyle です.
このように依存関係プロパティを定義してから,
LineGraph の既定 Style を Generic.xaml で次のように定義します.

ラベルや目盛など他のコントロールも同時に配置していますが,
今回のメインは GraphGridPath という名前を付けた Path オブジェクトです.
LineGraph.cs の静的なコンストラクタ内で
DefaultStyleKeyProperty.OverrideMetadata() メソッドをコールしているため,
Generic.xaml で定義した名前を FindName() メソッドで探し出すことができます.
FindName() での検索先が更新されるタイミングは OnApplyTemplate() イベントハンドラが最速らしいので,
このメソッドを次のように override します.

なんだかコードがいっぱいになってしまいました.
ついでに定義したコントロールもすべて読み込むようにしています.
ポイントは次の 2 点のみ.

  • IsApplyTemplate プロパティでテンプレート適用済確認できるようにした
  • MainContainerGrid のサイズ変更イベントハンドラでグラフ要素を更新するようにした

IsApplyTemplate というプライベートプロパティを定義し,
この OnApplyTemplate() を通ったかどうか確認できるようにしておきます.
これは,
依存関係プロパティのプロパティ変更のタイミングが,
OnApplyTemplate() が呼び出されるタイミングより速いため,
プロパティ変更のタイミングで XAML で定義したコントロールに対する操作を
おこなうときに NullReferenceException が発生しないようにするためのものです.
というわけで IsApplyTemplate プロパティは後で出てきます.

すべてを包括する MainContainerGrid にサイズ変更があった場合,
必ずグラフ要素を更新しなければならないため,
SizeChanged イベントハンドラを override しています.

ここまできてようやくグリッド線の描画にたどり着きました.
長かった….

BuildGraphGrid() メソッド内でグリッド線の描画に関するコードを記述します.

GeometryGroup を新規に生成して,
それぞれの軸に対するグリッド線を Children に追加しています.

ここに出てくる XAxisPositionFromValue() メソッドなどは,
描画すべき位置をコントロールのサイズと軸設定から算出してくれるヘルパで,
次のように定義しています.

以上.これでグリッド線が描画されます.
試しに見てみるには,
WPF アプリケーションプロジェクトのほうで
実際に View に配置してみます.

MainView.xaml はこんな感じ.

対する MainViewModel は次のようになります.

ちなみに ViewModelBase は INotifyPropertyChanged を実装した
NotifyPropertyChangedDataClass 抽象クラスと同じ内容です.
したがってここでも SetProperty() メソッドを使っています.

というわけで実行結果.
GridOnly_2

このままでは縦軸と第 2 主軸のグリッド線の見分けがつかないので
少し工夫しないといけませんが,
とりあえず基礎は完成.
なんか周辺要素が多くて予想以上に長くなった….