Gibt es eine Möglichkeit, Änderungen von a zu hören DependencyProperty
? Ich möchte benachrichtigt werden und einige Aktionen ausführen, wenn sich der Wert ändert, aber ich kann keine Bindung verwenden. Es gehört zu einer DependencyProperty
anderen Klasse.
80
Antworten:
Wenn es sich
DependencyProperty
um eine separate Klasse handelt, ist es am einfachsten, einen Wert daran zu binden und Änderungen an diesem Wert abzuhören.Wenn es sich bei dem DP um einen DP handelt, den Sie in Ihrer eigenen Klasse implementieren, können Sie beim Erstellen des einen PropertyChangedCallback registrieren
DependencyProperty
. Sie können dies verwenden, um Änderungen der Eigenschaft abzuhören.Wenn Sie mit einer Unterklasse arbeiten, können Sie OverrideMetadata verwenden , um
PropertyChangedCallback
dem DP einen eigenen hinzuzufügen , der anstelle eines ursprünglichen aufgerufen wird.quelle
Diese Methode fehlt hier definitiv:
DependencyPropertyDescriptor .FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton)) .AddValueChanged(radioButton, (s,e) => { /* ... */ });
quelle
descriptor.RemoveValueChanged(...)
DependencyPropertyDescriptor
gibt es eine statische Liste aller Handler in der Anwendung, sodass jedes Objekt, auf das im Handler verwiesen wird, ausläuft. Es funktioniert nicht wie ein gewöhnliches Ereignis.Ich habe diese Utility-Klasse geschrieben:
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); } }
quelle
Es gibt mehrere Möglichkeiten, dies zu erreichen. Hier ist eine Möglichkeit, eine abhängige Eigenschaft in eine beobachtbare Eigenschaft zu konvertieren, sodass sie mit System.Reactive abonniert werden kann :
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)); }); } }
Verwendung
Denken Sie daran, die Abonnements zu entsorgen, um Speicherverluste zu vermeiden:
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); } }
quelle
Sie könnten das Steuerelement erben, das Sie abhören möchten, und dann direkten Zugriff auf Folgendes haben:
protected void OnPropertyChanged(string name)
Kein Risiko eines Speicherverlusts.
Haben Sie keine Angst vor Standard-OO-Techniken.
quelle
Wenn das der Fall ist, ein Hack. Sie könnten eine statische Klasse mit einem einführen
DependencyProperty
. Ihre Quellklasse bindet auch an diesen dp und Ihre Zielklasse bindet auch an den DP.quelle