【WPF】FrameworkElement.DataContextプロパティの値が変わるタイミング。
- 2014.03.30 Sunday
- 14:23
【WPF】FrameworkElement.DataContextプロパティを無効にする方法
http://pro.art55.jp/?eid=1304213
前回は、DataContextに設定した値をクリアーし、親要素の値を再設定したい場合は、DependencyProperty.UnsetValueを設定するばよいという方法を紹介しましたが、今回はDataContextの値が変更されるタイミングを見ていきたいと思います。私の個人の意見としては、一度設定したDataContextの値を変えてしまうような実装は、WPFアプリケーションの設計としては、バグの元になりやすく、間違っていると思います。(伝搬して変更した子要素のDataContextも値の書き換えという事にはなりますが、それは対象外と・・・)
今回は、DataContextの値が変わるタイミングです。正直、私はこの点に関して意識したことは全くありませんでした。前述の通り書き換えないからです。必要なタイミングまでには、値が設定されていたので気にしていなかったということなのですが、DependencyProperty.UnsetValueを設定したタイミングで、その設定した要素や親要素を見に行っているような動きをしていたため、少し違和感を感じたのがきっかけです。親要素が変わったタイミングで子要素に伝搬するだけだと思っていたからです。
試してみました。
<Grid Background="Pink" x:Name="TopPanel" DataContextChanged="OnDataContextChanged">
<Grid Margin="40" Background="HotPink" x:Name="MiddlePanel" DataContextChanged="OnDataContextChanged">
<Grid Margin="40" x:Name="BottomPanel" Background="DeepPink" DataContextChanged="OnDataContextChanged">
<Button Margin="40" x:Name="Button" Click="OnClick" DataContextChanged="OnDataContextChanged">Click</Button>
</Grid>
</Grid>
</Grid>
private void OnClick(object sender, RoutedEventArgs e)
{
Debug.Print("BottomPanel.DataContext に20を設定する");
BottomPanel.DataContext = 20;
Debug.Print("TopPanel.DataContextに10を設定する");
TopPanel.DataContext = 10;
Debug.Print("BottomPanel.DataContextにDependencyProperty.UnsetValueを設定する");
BottomPanel.DataContext = DependencyProperty.UnsetValue;
Debug.Print("Buttonのコンテンツを書き換える。");
var button = (Button)sender;
button.Content = button.DataContext;
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var element = sender as FrameworkElement;
string message = string.Format("{0}.{1} = {2}", element.Name, e.Property.Name, e.NewValue);
// MessageBox.Show(message);
Debug.Print(message);
}
これを実行すると、以下の出力が得られます。
BottomPanel.DataContext に20を設定する
BottomPanel.DataContext = 20
Button.DataContext = 20
TopPanel.DataContextに10を設定する
TopPanel.DataContext = 10
MiddlePanel.DataContext = 10
BottomPanel.DataContextにDependencyProperty.UnsetValueを設定する
BottomPanel.DataContext = 10
Button.DataContext = 10
Buttonのコンテンツを書き換える。
VirualTreeの位置関係はXAMLの通り
TopPanel - MiddlePanel - BottomPanel - Button
まず、BottomPanelの値を変えると、
BottomPanelとその子のButtonのDataContextの値が変わっていることがわかります。
TopPanel - MiddlePanel - BottomPanel(20) - Button(20)
TopPanelの値を変えると、TopPanel とMiddlePanel まで値が変わります。
すでにBottomPanelに値が設定されているため、BottomPanel以降は伝搬しません。
TopPanel(10) - MiddlePanel(10) - BottomPanel(20) - Button(20)
BottomPanelをDependencyProperty.UnsetValueを設定することで、無効にします。
そうすると、そのタイミングで、BottomPanelは親要素のDataContextが反映されます。
そして、子要素(Button)にも伝搬します。
TopPanel(10) - MiddlePanel(10) - BottomPanel(無効) - Button(20)
TopPanel(10) - MiddlePanel(10) - BottomPanel(10) - Button(10)
以上のことから、DataContextはDataContextに値を設定したタイミングで、常に自分自身から子要素までの値が切り替わっていることが確認できました。私が想定したもう一つの可能性は、DataContextの値が必要となったタイミングで値が切り替わっているのではないかというものがあったのですが、そうではないようです。「見込生産」か「受注生産」かどちらなんだろうってちょいと思ったんですが、「見込生産」に該当するようです。
まとめ、
1.FrameworkElement.DataContextプロパティは、値が変わったタイミングで、
値が設定されていない直近の子要素まで伝搬する。
2.FrameworkElement.DataContextプロパティに、DependencyProperty.UnsetValueを設定すると
設定された要素は、親要素から値を取得し設定される(値の場合は値、nullの場合はnull)。
さらに1の通り、値が伝搬する。