WPF、怒りのツリー外DataContext伝播

この記事は [XAML Advent Calender 2014](http://qiita.com/advent-calendar/2014/xaml)の 12/10分の記事です。
自分のブログが復活していないので復活次第そちらへも同じものを書きます。
<h2>そもそもどういうこと？</h2>
WPFerには常識ですが、FrameworkElementに親子関係があればDataContextが継承されます。親のDataContextが変われば子のDataContextも自動で変わります。この機能のおかげで大分XAMLの記述量少なくなっていますよね。

![alt](http://i.gyazo.com/d6f671363e0d1278baa2d98d33843b77.png)

この機能は基本的に**「VisualTree(LogicalTreeの方かもしんない)上で親子関係があるFrameworkElement同士」**でしか機能しません。FrameworkElementじゃないとそもそもDataContextプロパティがありません。
てはではこれはどうなっているんでしょうか？

![alt](http://i.gyazo.com/0e3b9661168b32ed7758a9aee75aff62.png)

添付プロパティであるInteraction.Triggersも、トリガーであるEventTriggerも、トリガーアクションであるInvokeCommandActionもどれも全然FrameworkElementじゃありません。どころか起点となるInteraction.Triggersは添付プロパティなのでそもそもVisualTree上にあるものではありません。しかしこのコードは正常に動作し、MainWindowViewModelのTestCommandがきちんと呼び出されます。
実はこれにはWPFのアンドキュメントな仕様が関係しています。
<h2>Freezableの魔法</h2>
トリガーの基底クラスはTriggerBase<T>です。そしてトリガーアクションの基底クラスはTriggerAction<T>です。
*(どちらもBlendSDKのSystem.Windows.Interactivity.dllに含まれます)*

どちらもFrameworkElementではなく、ですのでDataContextプロパティ自体を持ちません。

TriggerBase<T>とTriggerAction<T>の基底クラスは実はFreezableです。
>[Freezable クラス - MSDN](http://msdn.microsoft.com/ja-jp/library/system.windows.freezable(v=vs.110).aspx)

実はこのFreezableであることがこの摩訶不思議なDataContextの伝播の肝になります。
>[Leveraging Freezables to Provide an Inheritance Context for Bindings - Dr.WPF](http://drwpf.com/blog/2008/05/22/leveraging-freezables-to-provide-an-inheritance-context-for-bindings/)

仕組みはまったくのブラックボックスで調べても調べてもわかりませんが、**FreezableであるとまるでVisualTree(LogicalTreeの方かもしんない)上にありDataContextが存在するかのようにバインドが可能になる**のです。Brushとかも基底はFreezableで、同じ仕組みでバインドを実現しています。
*(なお昔調べたところSilverlightでは何かのコレクション(名前覚えていない)に放り込むことで似た仕組みを実現していたのでもしかしたらStoreAppもそうなんじゃないかな？Blend SDKっぽいもの見ればたぶんわかります。僕はStoreAppに興味ないのでスルー)*

この仕組みが大変やっかいなのは、**MSのドキュメントのどこをどう探してもこの仕様が載っていない**事です。プログラマたるもの、破壊的変更がなされる余地を多く残すアンドキュメントな仕様には頼りたくないものなのですが・・。
<h2>Livet固有のMessageシステム</h2>
さて私がAuthorを務める[Livet](https://visualstudiogallery.msdn.microsoft.com/ee198b62-fa45-496c-9c1c-e8f63b43f677)はこのFreezableによるバインドの仕組みを使ってPrismやMVVM Light Toolkitにはない独自のMessenger機能を持っています。
<h3>LivetのMessenger</h3>
Messenger自体は以下の図のように、ViewModel側からViewを参照せずに、ViewModel起点でViewになんらかの処理を起こさせる機構の事です。*(この図ではPrismのMessengerについて説明しています)*
>![Messengerの仕組み](http://i.gyazo.com/964d6651316e771bdd393c3ad52ca9fb.png)
*[もう内容が古くなってしまってどうしてくれようかと思っている私の@IT記事](http://www.atmarkit.co.jp/fdotnet/chushin/greatblogentry_02/greatblogentry_02_03.html)から抜粋*

PrismやMVVM Lightのものと違いLivetのMessengerは、View側でダイレクトにMessageの定義ができます。

![Livet-DirectInteractionMessage](http://i.gyazo.com/ba74cbce309bf062153450fb18eef900.png)

この例に出てくるConfirmationDialogInteractionMessageActionは、通常ViewModelから発信されたMessageを受け取ってユーザーに確認ダイアログを表示し、ViewModel側のメソッドを呼び出すトリガーアクションです。そういったPrismのようなシナリオの他にLivetのトリガーアクションではView側で直接Messageを定義するというシナリオを用意してあります。
DirectInteractionMessageという部分で直接メッセージを定義しています。このView側での直接メッセージ定義のメリットはこういった確認ダイアログの表示の際などに威力を発揮します。たとえばこれをPrismで実現する場合を考えてみましょう。

![Prismでの確認ダイアログの表示](http://i.gyazo.com/6c3dd85cda660dd8e63df442b7d6a104.png)

見ての通り、たかだか確認ダイアログを表示するのにやり取りが多すぎです。LivetではViewに直接メッセージを定義できるおかげでこうなります。

![Livetでの確認ダイアログの表示](http://i.gyazo.com/323aa203024bec3e18baa9b8d82c4a44.png)

見ての通りシンプルになります。ViewModel側にわけのわからない粒度のメソッドが沢山できることもなくなるわけです。
素晴らしいですね！(@neuecc風自画自賛芸)

<h3>XAML以外でFreezableを使用することで起こる問題</h3>
さて、XAMLに書けるものならなんでもバインドしたくなるのがWPFer、いや、XAMLerの性(サガ)。我々を苦しめてやまないこの性ですが、当然私もそれに躍らされて結果Messageはバインド可能なオブジェクトになりました。さきほどの例でもすでにバインドしていますね。

かといってMessageをFrameworkElementにするなんていう悪手は使いたくありません。MessageはViewModelでも扱うものですし、ただでさえ小さいUIElementを大量に表示するのが苦手なXAML系テクノロジーではこれはどうみても悪手です。しかしMessageはトリガーアクションでもトリガーでもありません。じゃあ、ということで先ほどのFreezableを使用していたわけです。つまりLivetのMessageはすべてFreezableでした。この機能を盛り込んだのはもういつかわからないくらい昔のことです。(0.9x系のころからあったようななかったような)

それがですね。。。。。。。。。。。

>@Grabacr07 さん記事
[バックグラウンド スレッドで UI 要素を作るとメモリリークする (WPF) - grabacr.nét](http://grabacr.net/archives/1851)

>@karno　さん記事
[バックグラウンドスレッドでUI要素を作るともっと問題は深刻かもしれない。(WPF) - LOGarithm](http://b.starwing.net/?p=142)

この怒り、どこにぶつければ良いのでしょうか？
**FreezableをFreezeすれば別スレッドからも使えるとかいう仕様はどこいったんでしょうか？**

どうやらお二人にはLivetのMessageにもあるこの問題でご迷惑をおかけしたみたいです。

<h3>Livetでの対策<h3>

最初は上の記事を前職での忙しい最中にチラ見しただけで勘違いしていて、FreezableさえMessageから切り離せばいいと思っていたんです。
その方針でブランチコミットまでしています。
*(DataContextプロパティをDirectInteractionMessage以下にはやし、手動継承するやり方でやりました)*
>[LivetCommit - Removed Freezable classes from InteractionMessage. - Github](https://github.com/ugaya40/Livet/commit/0b0654f880a711bd1d3e146537f3f7592930cdda)

ですがよく見たら**DependencyObjectすべて**で起こる問題じゃないですか！
**DependencyObjectであることってのはバインドできるオブジェクトの絶対条件であり、これはつまりLivetのバインドできるMessageをUIスレッド以外から生成する道は詰んだという事を示します。**
(なおMessageをUIスレッド以外から作らなければ現状Livetでそれは回避可能です)

なのでまずMessageは別スレッドから生成しようとしても必ずUIスレッド上で動作する。あるいはMVVMの本来のあり方どおりにModelにUIスレッド以外をすべて押し込め、ViewModel以上はUIスレッド上で動くという制約を入れることの妥当性について検討している最中となっております。

次点のアプローチとしては、ViewModelで使用するMessageとViewで使用するMessageはクラスを分けるというやり方も検討しています。(ただこのやり方だときっと開発者がViewModelにある別のDependecyObjectをリークさせて不毛なんだろうな・・)

*(MVVMの考え方からすればWPFというPresentationPlatformの制約はViewModelまでを使って吸収されるべきです。そして実際ViewModelでDependencyObjectを使用することは決して少なくありません。なのでViewModelまでをUIスレッドに固定するという考え方は突拍子もないように聞こえますが妥当です。反論は[こちら](http://www.slideboom.com/presentations/591514)を読まれた後にどうぞ)*