In WPF when DataContext changes the DataContextChanged event fires. We could attach event handler to this event to execute some custom code.
In Silverligth 3 all is the same, except one small thing: DataContextCahnged is marked as internal… hmm... how then we could know what DataContext has changed?
We could do a simple trick. We can’t access DataContextChanged event, but we could bind some our dependency property to DataContext and handle dependency property changed event :)
Let’s build a small class which will do a binding for us.
public static class DataContextChangedEventManager
{
public delegate void DataContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args);
public static readonly DependencyProperty DataContextChangedProperty =
DependencyProperty.Register("DataContextChangedProperty", typeof(object), typeof(FrameworkElement), new PropertyMetadata(null, OnDataContextChanged));
public static readonly DependencyProperty DataContextChangedEventHandlersProperty =
DependencyProperty.Register("DataContextChangedEventHandlersProperty", typeof(EventHandler<DataContextChangedEventArgs>), typeof(FrameworkElement), new PropertyMetadata(null));
public static void OnDataContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var eventHandlers = obj.GetValue(DataContextChangedEventHandlersProperty) as EventHandler<DataContextChangedEventArgs>;
if (eventHandlers != null)
{
eventHandlers.Invoke(obj, new DataContextChangedEventArgs(args.NewValue));
}
}
...
}
public class DataContextChangedEventArgs : EventArgs
{
public DataContextChangedEventArgs(object value)
{
Value = value;
}
public object Value { get; private set; }
}
Here we are defining DataContextChangedProperty. This property will help us to react on DataContext changed event. When property has changed the OnDataContextChanged will be fired.
Also we have DataContextChangedEventHandlersProperty property defined which store event handlers.
Now let’s define method for attaching event handler for DataContextChanged event:
...
public static void AddDataContextChangedEventHandler(this FrameworkElement control, EventHandler<DataContextChangedEventArgs> value)
{
if (control.GetValue(DataContextChangedEventHandlersProperty) == null)
{
control.SetValue(DataContextChangedEventHandlersProperty, value);
}
else
{
var eventHandler = Delegate.Combine((Delegate)control.GetValue(DataContextChangedEventHandlersProperty), value);
control.SetValue(DataContextChangedEventHandlersProperty, eventHandler);
}
if (control.GetBindingExpression(DataContextChangedProperty) == null)
{
control.SetBinding(DataContextChangedProperty, new Binding());
}
}
...
And method for detaching event handlers:
...
public static void RemoveDataContextChangedEventHandler(this FrameworkElement control, EventHandler<DataContextChangedEventArgs> value)
{
if (control.GetValue(DataContextChangedEventHandlersProperty) != null)
{
var eventHandler = Delegate.Remove((Delegate)control.GetValue(DataContextChangedEventHandlersProperty), value);
control.SetValue(DataContextChangedEventHandlersProperty, eventHandler);
}
if (control.GetBindingExpression(DataContextChangedProperty) != null)
{
control.SetValue(DataContextChangedProperty, DependencyProperty.UnsetValue);
}
}
...
And the usage.
Lets say we have TextBlock and we want to show MessageBox when DataContext changes.
<TextBlock x:Name="TestBlock" DataContext="{Binding}"/>
We see here what there is binding defined on DataContext propertyof the control. This binding means what we bind DataContext of the control to current control DataContext. Really this does nothing but we need it to get our DataContextChangedProperty binding to work.
And in code we need attach event handler for DataContext changed event.
public DataContextChanged()
{
InitializeComponent();
TestBlock.AddDataContextChangedEventHandler(OnDataContextChanged);
}
private void OnDataContextChanged(object sender, DataContextChangedEventArgs args)
{
MessageBox.Show(args.Value.ToString());
}
And working example as usual:
The workarounds library with examples could be downloaded from here (VS2010): Andrew Veresov Workarounds library v1.0