Có cách nào để lắng nghe những thay đổi của a DependencyProperty
? Tôi muốn được thông báo và thực hiện một số hành động khi giá trị thay đổi nhưng tôi không thể sử dụng ràng buộc. Nó là một DependencyProperty
lớp khác.
Câu trả lời:
Nếu đó là một DependencyProperty
lớp riêng biệt, cách dễ nhất là liên kết một giá trị với nó và lắng nghe những thay đổi trên giá trị đó.
Nếu DP là DP mà bạn đang triển khai trong lớp của riêng mình, thì bạn có thể đăng ký một PropertyChangedCallback khi tạo DependencyProperty
. Bạn có thể sử dụng điều này để lắng nghe những thay đổi của tài sản.
Nếu bạn đang làm việc với một lớp con, bạn có thể sử dụng OverrideMetadata để thêm PropertyChangedCallback
DP của riêng bạn vào DP sẽ được gọi thay vì bất kỳ lớp gốc nào.
Phương pháp này chắc chắn bị thiếu ở đây:
DependencyPropertyDescriptor
.FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton))
.AddValueChanged(radioButton, (s,e) => { /* ... */ });
descriptor.RemoveValueChanged(...)
DependencyPropertyDescriptor
này có danh sách tĩnh của tất cả các trình xử lý trong ứng dụng, vì vậy mọi đối tượng được tham chiếu trong trình xử lý sẽ bị rò rỉ. Nó không hoạt động giống như sự kiện thông thường.
Tôi đã viết lớp tiện ích này:
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);
}
}
Có nhiều cách để đạt được điều này. Đây là một cách để chuyển đổi một thuộc tính phụ thuộc thành một thuộc tính có thể quan sát được, để nó có thể được đăng ký bằng cách sử dụng System.Reactive :
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));
});
}
}
Sử dụng
Hãy nhớ loại bỏ các đăng ký để tránh rò rỉ bộ nhớ:
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);
}
}
Nếu đó là trường hợp, Một hack. Bạn có thể giới thiệu một lớp Tĩnh với một DependencyProperty
. Lớp nguồn của bạn cũng liên kết với dp đó và lớp đích của bạn cũng liên kết với DP.