Detecting dependency property changes in WinRT

person Thomas Levesquefolder_openTricks, WinRTlocal_offer, , , , access_time April 21, 2013

Today I’d like to share a trick I used while developing my first Windows Store application. I’m very new to this technology and it’s my first article about it, so I hope I won’t make a fool of myself…

It’s often useful to be notified when the value of a dependency property changes; many controls expose events for that purpose, but it’s not always the case. For instance, recently I was trying to detect when the Content property of a ContentControl changed. In WPF, I would have used the DependencyPropertyDescriptor class, but it’s not available in WinRT.

Fortunately, there is a mechanism which is available on all XAML platforms, and can solve this problem: binding. So, the solution is just to create a class with a dummy property that is bound to the property we want to watch, and call a handler when the value of the dummy property changes. To make it cleaner and hide the actual implementation, I wrapped it as an extension method that returns an IDisposable:

    public static class DependencyObjectExtensions
    {
        public static IDisposable WatchProperty(this DependencyObject target,
                                                string propertyPath,
                                                DependencyPropertyChangedEventHandler handler)
        {
            return new DependencyPropertyWatcher(target, propertyPath, handler);
        }

        class DependencyPropertyWatcher : DependencyObject, IDisposable
        {
            private DependencyPropertyChangedEventHandler _handler;

            public DependencyPropertyWatcher(DependencyObject target,
                                             string propertyPath,
                                             DependencyPropertyChangedEventHandler handler)
            {
                if (target == null) throw new ArgumentNullException("target");
                if (propertyPath == null) throw new ArgumentNullException("propertyPath");
                if (handler == null) throw new ArgumentNullException("handler");

                _handler = handler;

                var binding = new Binding
                {
                    Source = target,
                    Path = new PropertyPath(propertyPath),
                    Mode = BindingMode.OneWay
                };
                BindingOperations.SetBinding(this, ValueProperty, binding);
            }

            private static readonly DependencyProperty ValueProperty =
                DependencyProperty.Register(
                    "Value",
                    typeof(object),
                    typeof(DependencyPropertyWatcher),
                    new PropertyMetadata(null, ValuePropertyChanged));

            private static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var watcher = d as DependencyPropertyWatcher;
                if (watcher == null)
                    return;

                watcher.OnValueChanged(e);
            }

            private void OnValueChanged(DependencyPropertyChangedEventArgs e)
            {
                var handler = _handler;
                if (handler != null)
                    handler(this, e);
            }

            public void Dispose()
            {
                _handler = null;
                // There is no ClearBinding method, so set a dummy binding instead
                BindingOperations.SetBinding(this, ValueProperty, new Binding());
            }
        }
    }

It can be used like this:

// Subscribe
watcher = myControl.WatchProperty("Content", myControl_ContentChanged);

// Unsubscribe
watcher.Dispose();

I hope you will find this useful!

Comments

  1. Can you give me an example in real code on how this is used? I’m missing something on the implementation.

      1. outstanding…thank you.

  2. Is it possible to trace attached properties this way? Currently i’m trying to find the way to trace AP changes…

    1. Thomas Levesque

      @PantheR, attached properties are just a special case of dependency properties, so in theory it should be possible… However I’m not sure how to specify the path; you have to include the name of the type that exposes the property, and since you’re not in a XAML context, you can’t use xmlns prefixes.

  3. So you are binding to a binding? Yikes. There may be no other way, but it’s important to recognize the performance impact of bindings – let alone cascaded bindings. Clever, but dangerous. /J

    1. Thomas Levesque

      Yes, I know it’s not ideal… but it came in handy a few times when I really needed to react to the changes of a property and no event was available.

  4. Could you not just use DependencyObject.RegisterPropertyChangedCallback?

    1. Thomas Levesque

      Hi David,

      Sure, I could…except that RegisterPropertyChangedCallback was introduced in Windows 10 universal apps, and didn’t exist when I wrote this article 😉

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>