WPFのBindingマークアップ拡張の実装を見てみる
この前書いたイベントのマークアップ拡張ですが、記事の最後に書いた通り一部の動作に制限があります。
DataContextの変更に追従せず、マークアップ拡張のProvideValueメソッドが呼ばれたときのDataContextのコマンドをずっと呼び出してしまう。などなど。
実用上で問題になりそうな箇所が残ってます。
これは、ProvideValueメソッドの実装の中で、そのメソッド呼び出し時点でのDataContextに対し、指定された名前のコマンドをリフレクションで取得してずっと使い続けているために起きています。
ちゃんとデータバインディングを使った書き方になっていないために起きている問題ですね。
ということで、そもそもWPFのBindingマークアップ拡張ってどういう実装になってるのか??を少し調べてみました。
MSの公開しているリファレンスソースを探してみます。
Reference Source
Bindingのリファレンス実装を探す
マークアップ拡張って、サフィックスとして名前の最後に○○Extensionみたいな名前にするのが作法なので、BindingExtensionってのがないかずっと探してました。
実際に、StaticResourceExtensionやTemplateBindingExtensionなどはすぐに見つかりましたが、お目当てのBindingExtensionなんてクラスが見当たらない。。。
小一時間探し回った結果。。。
ありました!!↓コイツです。
http://referencesource.microsoft.com/#PresentationFramework/Framework/System/Windows/Data/Binding.cs
C#のコード側からバインディングを行うとき等に使っているBindingクラスです。
Bindingクラス自身が実はMarkupExtensionを実装していて、XAML上の{Binding ○○}というマークアップ拡張のバインディング構文で行われる処理が定義されています。
ちなみに、Binding系のクラスは以下のような継承関係になってます。
Bindingマークアップ拡張のProvideValue実装
{Binding ○○}と書いた時に、どんな処理が行われるのかを見るために、ProvideValueの実装を追いかけてみます。
BindingクラスのProvideValueメソッドは、抽象クラスのBindingBaseクラス内で実装されてます。
BindingBase ProvideValueメソッド
ここから先、まだ詳細までは読めてないですが。。。
以下二つの部分が肝のようです。
バインディングの仕組みをしっかり追いかける時は、この辺のソースを見てみればよいかと思います。
IProvideValueTargetのチェック
・ProvideValueメソッドの201行目
Helperクラスを用いて、マークアップ拡張適用の対象となるIProvideValueTargetが、Bindingマークアップ拡張構文の結果を受け取れるものか確認をしています。
Helperクラス CheckCanReceiveMarkupExtensionメソッド
BindingExpressionの作成
・ProvideValueメソッドの209行目
ProvideValueメソッドの戻り値となるBindingExpressionをここで作成しています。
CreateBindingExpressionOverideという仮想関数を呼び出しをして戻り値をそのまま返すだけです。
Bindingマークアップ拡張の場合は、Bindingクラス側のメソッドが呼び出され、BindingExpressionクラスのCreateBindingExpressionメソッド呼び出しの箇所までたどり着きます。
ここで、BindingExpressionを作り、ProvideValueメソッドの戻り値となります。
BindingExpression CreateBindingExpressionメソッド
リファレンスソースをちゃんと追いかけてみると、XAMLの仕組みが色々見えてきて面白いですねw