DataContextの変更を検知する
たまーに、DataContextが変わったかどうかをチェックしたいことがあるのですが、Silverlight には DataContextChanged のイベントが無いのです。そもそも、DependencyProperty の変更イベントがとれないのがちょっと悲しいです。
で、DataContext の変更を検知するクラスを作りました。
public class DataContextChangedNotifier{ public event EventHandler<DataContextChangedEventArgs> DataContextChanged; public DataContextChangedNotifier(FrameworkElement element){ element.SetBinding(DataContextProperty, new Binding()); element.SetValue(InstanceProperty, this); } public static readonly DependencyProperty InstanceProperty = DependencyProperty.RegisterAttached("Instance", typeof(DataContextChangedNotifier), typeof(DataContextChangedNotifier), null); public static readonly DependencyProperty DataContextProperty = DependencyProperty.Register("DataContext", typeof(Object), typeof(FrameworkElement), new PropertyMetadata(OnDataContextChanged)); private static void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { var element = sender as FrameworkElement; if(element != null){ var notifier = element.GetValue(InstanceProperty) as DataContextChangedNotifier; if(notifier != null && notifier.DataContextChanged != null){ notifier.DataContextChanged(notifier,new DataContextChangedEventArgs(element,e.NewValue)); } } } } public class DataContextChangedEventArgs : EventArgs{ public DataContextChangedEventArgs(FrameworkElement source,object dataContext){ this.Source = source; this.DataContext = dataContext; } public FrameworkElement Source { get; private set; } public object DataContext { get; private set; } }
仕組みは簡単で、変更を受け取りたい FrameworkElement に Binding を設定して、バインド先を DataContextChangedNotifier にしています。この時、Binding にパスなどを設定していないので、FrameworkElement の DataContext が変更されたら、その値が DataContextChangedNotifier の DataContextProperty に設定されるって動きになります。
使い方は以下のような感じ。
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); var notifier = new DataContextChangedNotifier(this); notifier.DataContextChanged += notifier_DataContextChanged; this.DataContext = new Object(); } void notifier_DataContextChanged(object sender, DataContextChangedEventArgs e) { MessageBox.Show("DataContext Changd!"); } }
どんな場合でもちゃんといけるかは見てませんが(ItemsControlのItemsSourceとか)、まぁ多くの場合はいけそうな気がしてます。