Detecting dependency property changes in WinRT
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!