Escuchar cambios de propiedad de dependencia

¿Hay alguna manera de escuchar los cambios de DependencyProperty ? Quiero recibir notificaciones y realizar algunas acciones cuando el valor cambie, pero no puedo usar el enlace. Es una DependencyProperty de otra clase.

Si se trata de una DependencyProperty de una clase separada, la forma más fácil es vincular un valor y escuchar los cambios en ese valor.

Si el DP es uno que está implementando en su propia clase, puede registrar un PropertyChangedCallback cuando crea DependencyProperty . Puede usar esto para escuchar los cambios de la propiedad.

Si está trabajando con una subclase, puede usar OverrideMetadata para agregar su propio PropertyChangedCallback al DP que se llamará en lugar de uno original.

Este método definitivamente falta aquí:

 DependencyPropertyDescriptor .FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton)) .AddValueChanged(radioButton, (s,e) => { /* ... */ }); 

Escribí esta clase de utilidad:

  • Le da a DependencyPropertyChangedEventArgs valor antiguo y nuevo.
  • La fuente se almacena en una referencia débil en el enlace.
  • No estoy seguro de si exponer Binding & BindingExpression es una buena idea.
  • Sin fugas.
 using System; using System.Collections.Concurrent; using System.Windows; using System.Windows.Data; public sealed class DependencyPropertyListener : DependencyObject, IDisposable { private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); private static readonly DependencyProperty ProxyProperty = DependencyProperty.Register( "Proxy", typeof(object), typeof(DependencyPropertyListener), new PropertyMetadata(null, OnSourceChanged)); private readonly Action onChanged; private bool disposed; public DependencyPropertyListener( DependencyObject source, DependencyProperty property, Action onChanged = null) : this(source, Cache.GetOrAdd(property, x => new PropertyPath(x)), onChanged) { } public DependencyPropertyListener( DependencyObject source, PropertyPath property, Action 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 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 onChanged = null) { return new DependencyPropertyListener(source, property, onChanged); } } 

Hay múltiples formas de lograr esto. Aquí hay una forma de convertir una propiedad dependiente en una observable, de modo que pueda suscribirse a ella utilizando System.Reactive :

 public static class DependencyObjectExtensions { public static IObservable Observe(this T component, DependencyProperty dependencyProperty) where T:DependencyObject { return Observable.Create(observer => { EventHandler update = (sender, args) => observer.OnNext(args); var property = DependencyPropertyDescriptor.FromProperty(dependencyProperty, typeof(T)); property.AddValueChanged(component, update); return Disposable.Create(() => property.RemoveValueChanged(component, update)); }); } } 

Uso

Recuerde eliminar las suscripciones para evitar memory leaks:

 public partial sealed class MyControl : UserControl, IDisposable { public MyControl() { InitializeComponent(); // this is the interesting part var subscription = this.Observe(MyProperty) .Subscribe(args => { /* ... */})); // the rest of the class is infrastructure for proper disposing Subscriptions.Add(subscription); Dispatcher.ShutdownStarted += DispatcherOnShutdownStarted; } private IList Subscriptions { get; } = new List(); private void DispatcherOnShutdownStarted(object sender, EventArgs eventArgs) { Dispose(); } Dispose(){ Dispose(true); } ~MyClass(){ Dispose(false); } bool _isDisposed; void Dispose(bool isDisposing) { if(_disposed) return; foreach(var subscription in Subscriptions) { subscription?.Dispose(); } _isDisposed = true; if(isDisposing) GC.SupressFinalize(this); } } 

Puede heredar el Control que está tratando de escuchar y luego tener acceso directo a:

 protected void OnPropertyChanged(string name) 

Sin riesgo de pérdida de memoria.

No tengas miedo de las técnicas estándar de OO.

Si ese es el caso, uno piratea. Podría introducir una clase estática con DependencyProperty . Tu clase fuente también se une a ese dp y tu clase de destino también se une al DP.