The FrameworkElement.DataContext
property is a fundamental part of the XAML data-binding system and allows an object to be associated with a top-level element in the visual tree and then inherited by descendants of that element.
Unfortunately, the Windows Phone SDK does not include a public event for detecting when the DataContext
of a FrameworkElement
is changed. As a workaround, I have included in the downloadable sample code a class named DataContextChangedListener
that uses attached properties to emulate a DataContextChanged
event, which allows you to receive notification when a FrameworkElement
’s data context is changed (see Listing 26.4).
The Subscribe
method of the DataContextChangedListener
associates a PropertyChangedCallback
delegate with a specified FrameworkElement
. The delegate is invoked when a change to the DataContextProperty
is detected.
The HandleDataContextChanged
handler is called when the FrameworkElement
object’s DataContext
is changed because changing the DataContext
of the FrameworkElement
causes all bindings for the FrameworkElement
to be reevaluated.
The fact that the DataContextProperty
is named DataContextProperty is arbitrary and has no effect on the association between the attached property and the FrameworkElement.DataContext
property.
public class DataContextChangedListener
{
static readonly DependencyProperty DataContextProperty
= DependencyProperty.RegisterAttached(
"DataContextProperty",
typeof(object),
typeof(FrameworkElement),
new PropertyMetadata(HandleDataContextChanged));
static readonly DependencyProperty HandlersProperty
= DependencyProperty.RegisterAttached(
"HandlersProperty",
typeof(PropertyChangedCallback),
typeof(FrameworkElement),
new PropertyMetadata(
(object)((PropertyChangedCallback)(delegate { }))));
public static void Subscribe(
FrameworkElement element, PropertyChangedCallback handler)
{
ArgumentValidator.AssertNotNull(element, "element");
ArgumentValidator.AssertNotNull(handler, "handler");
PropertyChangedCallback handlers
= (PropertyChangedCallback)element.GetValue(HandlersProperty);
handlers += handler;
element.SetValue(HandlersProperty, handlers);
if (element.GetBindingExpression(DataContextProperty) == null)
{
element.SetBinding(DataContextProperty, new Binding());
}
}
public static void Unsubscribe(
FrameworkElement element, PropertyChangedCallback handler)
{
ArgumentValidator.AssertNotNull(element, "element");
ArgumentValidator.AssertNotNull(handler, "handler");
PropertyChangedCallback handlers
= (PropertyChangedCallback)element.GetValue(HandlersProperty);
handlers -= handler;
element.SetValue(HandlersProperty, handlers);
}
static void HandleDataContextChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
PropertyChangedCallback handlers
= (PropertyChangedCallback)element.GetValue(HandlersProperty);
PropertyChangedCallback tempEvent = handlers;
if (tempEvent != null)
{
tempEvent(d, e);
}
}
}
The ValidationSummary
control uses the DataContextChangedListener
to subscribe to the INotifyDataErrorInfo.ErrorsChanged
event of the viewmodel.
When the control is loaded, the control uses the static DataContextChangedListener.Subscribe
method to add its HandleDataContextChanged
method to the list of handlers.
If and when the handler is called, the control unsubscribes from the previous INotifyDataErrorInfo
and subscribes to the new object’s event. See the following excerpt:
void HandleLoaded(object sender, System.Windows.RoutedEventArgs e)
{
...
DataContextChangedListener.Subscribe(this, HandleDataContextChanged);
}
void HandleDataContextChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
/* Unsubscribe to previous notifier. */
INotifyDataErrorInfo oldNotifier = e.OldValue as INotifyDataErrorInfo;
if (oldNotifier != null)
{
oldNotifier.ErrorsChanged -= HandleErrorsChanged;
}
/* When the DataContext is an INotifyDataErrorInfo,
* monitor it for errors. */
INotifyDataErrorInfo notifier = DataContext as INotifyDataErrorInfo;
if (notifier != null)
{
notifier.ErrorsChanged += HandleErrorsChanged;
}
}
Hopefully, the FrameworkElement.DataContextChanged
event will be made public in a later release of the Windows Phone SDK; in the meantime, however, the custom DataContextChangedListener
is a simple solution that can be used wherever we need to respond to a change of an element’s data context.
18.226.180.68