今日はその第一弾。
WPF の ComboBox って、一度ドロップダウンを開けてしまうと DataContext に結びついた ViewModel を握ったまま放してくれなくなるようです。
何かの View (Window とか UserControl とか) の上に ComboBox を載せて、VIew の DataContext に View Model のインスタンス①を設定すると、普通に ComboBox の DataContext もそのインスタンス①になります。
新しい View Model のインスタンス②を用意して、View の DataContext をそのインスタンス②にしたら、当然 ComboBox の DataContext もインスタンス②になって、誰も参照していないインスタンス①はガベージコレクションの対象になるはず。
ところがインスタンス①が結びついている時に ComboBox のドロップダウンを開けてしまうと、ComboBox の ScrollViewer の Grid の Popup の PopupRoot の DataContext がインスタンス①で固定化されてしまって、その後 View や ComboBox の DataContext をインスタンス②にしても PopupRoot の DataContext はインスタンス①のままになってしまいます。
https://connect.microsoft.com/VisualStudio/feedback/details/796702/wpf-combobox-memory-leak によると、Microsoftさんは修正しない方針のようで。
色々調べましたが、回避方法は見つかりません。
View の View Model を取り替えずに済むようなアーキテクチャにするくらいですかね。
ただ、インスタンス①が結びついたまま残っていると言っても、下の図のような感じで結びついているのは PopupRoot だけで、ComboBox 自体は新しいインスタンス②を DataContext として持っています。
なので ComboBox のドロップダウンの中の項目が更新されない、とか、新しい View Model の機能が使えない、というわけではないのでご安心を。
メモリリークといえばメモリリークですが、最大でも画面内の ComboBox の数+1個の View Model のインスタンスが残ってしまうだけで、画面を開くたびに新しい View Model ができてインスタンス数が無限に増えていく、というわけではないので致命的ではなさそう。
懸念事項が一つだけあって、それはガベージコレクションされていると思っていた古い View Model のインスタンスが、頑固に生き残って想定外のイベントハンドリングとかで静的なデータを更新してしまったりするんじゃないかと。
そうならないように、この WPF のバグを理解した上で使わないといけませんね。
あ、ComboBox だけでなくドロップダウン式のコントロール一般のバグらしいです。
調べていないですが Menu とかもそうかなあ。