Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listen to changes of dependency property

Is there any way to listen to changes of a DependencyProperty? I want to be notified and perform some actions when the value changes but I cannot use binding. It is a DependencyProperty of another class.

like image 339
Rasto Avatar asked Jan 21 '11 23:01

Rasto


People also ask

What is a dependency property?

A dependency property is a specific type of property where the value is followed by a keen property system which is also a part of the Windows Runtime App. A class which defines a dependency property must be inherited from the DependencyObject class.

What is the biggest feature of dependency property?

Arguably the biggest feature of a dependency property is its built-in ability to provide change notification. The motivation for adding such intelligence to properties is to enable rich functionality directly from declarative markup.

Why do we need dependency properties?

The purpose of dependency properties is to provide a way to compute the value of a property based on the value of other inputs, such as: System properties, such as themes and user preference. Just-in-time property determination mechanisms, such as data binding and animations/storyboards.

How does dependency property work internally?

A DependencyProperty maintains a static reference of all the DependencyProperty you register in WPF object hierarchy. It maintains a HashTable named PropertyFromName which it uses internally to get the DependencyProperty object. So in other word, each dependencyProperty object is registered in a global HashTable.


3 Answers

This method is definitely missing here:

DependencyPropertyDescriptor     .FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton))     .AddValueChanged(radioButton, (s,e) => { /* ... */ }); 
like image 194
H.B. Avatar answered Oct 08 '22 05:10

H.B.


If it's a DependencyProperty of a separate class, the easiest way is to bind a value to it, and listen to changes on that value.

If the DP is one you're implementing in your own class, then you can register a PropertyChangedCallback when you create the DependencyProperty. You can use this to listen to changes of the property.

If you're working with a subclass, you can use OverrideMetadata to add your own PropertyChangedCallback to the DP that will get called instead of any original one.

like image 39
Reed Copsey Avatar answered Oct 08 '22 04:10

Reed Copsey


I wrote this utility class:

  • It gives DependencyPropertyChangedEventArgs with old & new value.
  • The source is stored in a weak reference in the binding.
  • Not sure if exposing Binding & BindingExpression is a good idea.
  • No leaks.
using System;
using System.Collections.Concurrent;
using System.Windows;
using System.Windows.Data;

public sealed class DependencyPropertyListener : DependencyObject, IDisposable
{
    private static readonly ConcurrentDictionary<DependencyProperty, PropertyPath> Cache = new ConcurrentDictionary<DependencyProperty, PropertyPath>();

    private static readonly DependencyProperty ProxyProperty = DependencyProperty.Register(
        "Proxy",
        typeof(object),
        typeof(DependencyPropertyListener),
        new PropertyMetadata(null, OnSourceChanged));

    private readonly Action<DependencyPropertyChangedEventArgs> onChanged;
    private bool disposed;

    public DependencyPropertyListener(
        DependencyObject source, 
        DependencyProperty property, 
        Action<DependencyPropertyChangedEventArgs> onChanged = null)
        : this(source, Cache.GetOrAdd(property, x => new PropertyPath(x)), onChanged)
    {
    }

    public DependencyPropertyListener(
        DependencyObject source, 
        PropertyPath property,
        Action<DependencyPropertyChangedEventArgs> onChanged)
    {
        this.Binding = new Binding
        {
            Source = source,
            Path = property,
            Mode = BindingMode.OneWay,
        };
        this.BindingExpression = (BindingExpression)BindingOperations.SetBinding(this, ProxyProperty, this.Binding);
        this.onChanged = onChanged;
    }

    public event EventHandler<DependencyPropertyChangedEventArgs> Changed;

    public BindingExpression BindingExpression { get; }

    public Binding Binding { get; }

    public DependencyObject Source => (DependencyObject)this.Binding.Source;

    public void Dispose()
    {
        if (this.disposed)
        {
            return;
        }

        this.disposed = true;
        BindingOperations.ClearBinding(this, ProxyProperty);
    }

    private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var listener = (DependencyPropertyListener)d;
        if (listener.disposed)
        {
            return;
        }

        listener.onChanged?.Invoke(e);
        listener.OnChanged(e);
    }

    private void OnChanged(DependencyPropertyChangedEventArgs e)
    {
        this.Changed?.Invoke(this, e);
    }
}

using System;
using System.Windows;

public static class Observe
{
    public static IDisposable PropertyChanged(
        this DependencyObject source,
        DependencyProperty property,
        Action<DependencyPropertyChangedEventArgs> onChanged = null)
    {
        return new DependencyPropertyListener(source, property, onChanged);
    }
}
like image 39
Johan Larsson Avatar answered Oct 08 '22 06:10

Johan Larsson