Freezable と Binding の関係がよく分からない
XAMLにうまく埋め込めるクラスを定義しようとしていて、Freezable が色々と謎。
例えば Hoge クラスとか Piyo クラスとか作って、下記のようなXAMLでインスタンス生成が出来るようにしたいとする。
<Hoge> <Hoge.Piyo> <Piyo Buzz={Binding Path=...}/> </Hoge.Piyo> </Hoge>
まず最初は {Binding Path=...} がうまく機能しなくて悩んだ。
とりあえず、Hoge や Piyo を FrameworkElement または FrameworkContentElement の派生クラスにすればうまく動くことが分かった。ああそりゃそうか、と腑に落ちるものがあった。ミソは、FrameworkElement や FrameworkContentElement には論理ツリーの親子関係を保持する機能が備わっていることだろう。Binding のためには親要素の DataContext プロパティが継承される必要があるわけで、プロパティを継承するためには親要素へのリンクを保持していなければいけない、というのは道理である。あるいは Binding.Source として ElementName を設定する場合なら、要素名で element が検索可能な論理ツリーに組み込まれていなければいけない、これも道理である。上記XAMLの場合は、Hoge オブジェクトに Piyo オブジェクトを AddLogicalChild() すれば論理ツリーに組み込むことが出来る。ふむふむ、なるほどね。
しかしだ、場合によっちゃ FrameworkElement やら FrameworkContentElement を継承するのはあんまり好ましくない。これらにはマウスやキーボードのイベントなども定義されているけど、コントロールとしてウィンドウに表示するものでなければこれらのイベントは不要だからだ。
そこで調べてみると、どうやら代わりに Freezable クラスを継承すれば良いらしいと分かった。例えば Brush クラスなどは Freezable の派生として定義されているようだ。試してみると確かに Binding が動作している。結果には満足だが、しかし理屈が分からない。Freezable は論理ツリーに組み込まれるわけでもないのに、何故 Binding が出来るのだろう?XAMLパーサーが何かやっているの?そもそも「フリーズ可能」であることと Binding 可能であることは何か関係があるの?
更に一歩進んで、次のように Piyo がコレクションになっている場合も実現したいとする。
<Hoge> <Hoge.PiyoCollection> <Piyo Buzz={Binding Path=...}/> <Piyo Buzz={Binding Path=...}/> <Piyo Buzz={Binding Path=...}/> ... </Hoge.PiyoCollection> </Hoge>
試行錯誤してみて分かったこと。
- PiyoCollection は FreezableCollection
でなければいけない。 - PiyoCollection は(read-onlyの)DependencyProperty でなければいけない。
この2点を守れば Binding は正しく動くことを確かめた。結果には満足だが、やはり道理が分からない。道理が分からぬものは何処かに落とし穴がありそうで怖い。
どーなってんの?