Detecting a Change of Data Context

,

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.

LISTING 26.4. DataContextChangedListener Class


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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.180.68