C#+WPFチューニング戦記

C#とWPFで高速なコードと最適なシステムを書くためにやってきたいろいろな事を書いてみます。.NET Frameworkのソースコードを読み解きましょう。なお、ここに書かれているのは個人の見解であって何らかの団体や企業の見解を代表するものではありません。

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 一択ということのようですね。

ここまで書いておいてなんですが、何か大きな勘違いをしてないか不安です。