FreezableにDataContextが伝播する仕掛け(その2)
素早く実証コードを書いてみました。
ViewModelとしては概ねこんな感じ
using System; using System.Windows; using System.Windows.Input; using System.Diagnostics; namespace WpfApplication1 { class MainWindowViewModel : DependencyObject { public string MessageText{ get { return (string)GetValue(MessageTextProperty); } set { SetValue(MessageTextProperty, value); } } public static readonly DependencyProperty MessageTextProperty = DependencyProperty.Register("MessageText", typeof(string), typeof(MainWindowViewModel), new PropertyMetadata("試験")); } class ShowMessageCommand : Freezable, ICommand { protected override Freezable CreateInstanceCore(){ return new ShowMessageCommand(); } public bool CanExecute(object parameter){return true;} public event EventHandler CanExecuteChanged; public void Execute(object parameter){ MessageBox.Show(Message); } public string Message{ get { return (string)GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } public static readonly DependencyProperty MessageProperty = DependencyProperty.Register("Message", typeof(string), typeof(ShowMessageCommand), new PropertyMetadata(string.Empty, (d, e) =>{ Debug.Fail("ここで止めると、StackTrace取れます。"); })); } }
XAMLはこんな感じ
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <StackPanel> <Button> ボタン <Button.Command> <local:ShowMessageCommand Message="{Binding MessageText}"/> </Button.Command> </Button> </StackPanel> </Window>
止まったところでデバッガに入り、スタックトレースを全部見る(当然ですが「外部コードを表示」のチェックはON)と通過箇所はわかります。
重要なポイントはここ。
WindowsBase.dll!System.Windows.DependencyObject.OnInheritanceContextChanged(System.EventArgs args) WindowsBase.dll!System.Windows.Freezable.AddInheritanceContext(System.Windows.DependencyObject context, System.Windows.DependencyProperty property) WindowsBase.dll!System.Windows.DependencyObject.ProvideSelfAsInheritanceContext(System.Windows.DependencyObject doValue, System.Windows.DependencyProperty dp)
Microsoftのリファレンスソースを読めば、DependencyObjectがFreezableだけ特別扱いして、継承属性を持つコンテキストをまるっと引き渡していることが読めるはずです。
とりあえず、「仕掛けがわからない」は解消できたのではないでしょうか。
問題は、Microsoftがこのあたりをprivateやinternalで固めているため、いつ何時実装を変更しても文句を言えないというところでしょう。
それにしても、これでは XAMLで扱うものは、FrameworkElement あたりを使わない場合は Freezable 一択ということのようですね。
ここまで書いておいてなんですが、何か大きな勘違いをしてないか不安です。