Прослушивание изменений свойства зависимостей
есть ли способ слушать изменения a DependencyProperty
? Я хочу знать и выполнять некоторые действия при изменении значения, но я не могу использовать привязку. Это DependencyProperty
другого класса.
6 ответов:
Если это
DependencyProperty
из отдельного класса самый простой способ-привязать к нему значение и прослушать изменения этого значения.Если DP-это тот, который вы реализуете в своем собственном классе, то вы можете зарегистрировать PropertyChangedCallback при создании
DependencyProperty
. Вы можете использовать это для прослушивания изменений свойства.Если вы работаете с подклассом, вы можете использовать OverrideMetadata чтобы добавить свой собственный
PropertyChangedCallback
к DP, который получит вызывается вместо любого оригинального.
этот метод определенно отсутствует здесь:
DependencyPropertyDescriptor .FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton)) .AddValueChanged(radioButton, (s,e) => { /* ... */ });
Я написал этот служебный класс:
- это дает DependencyPropertyChangedEventArgs со старым и новым значением.
- источник хранится в слабой ссылке в привязке.
- не уверен, что разоблачение Binding & BindingExpression является хорошей идеей.
- никаких утечек.
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); } }
есть несколько способов добиться этого. Вот способ преобразования зависимого свойства в наблюдаемое, чтобы его можно было подписаться на использование система.Реактивный:
public static class DependencyObjectExtensions { public static IObservable<EventArgs> Observe<T>(this T component, DependencyProperty dependencyProperty) where T:DependencyObject { return Observable.Create<EventArgs>(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)); }); } }
использование
Не забудьте удалить подписки, чтобы предотвратить утечку памяти:
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<IDisposable> Subscriptions { get; } = new List<IDisposable>(); 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); } }