2011-01-21 40 views
62

Có cách nào để nghe các thay đổi của DependencyProperty không? 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à DependencyProperty của một lớp khác.Nghe các thay đổi của thuộc tính phụ thuộc

+0

Tại sao bạn nói bạn không thể sử dụng tính ràng buộc? –

Trả lời

49

Nếu đó là DependencyProperty của một lớp riêng biệt, cách dễ nhất là liên kết giá trị với nó và lắng nghe các thay đổi về giá trị đó.

Nếu DP là một trong những bạn đang thực hiện trong lớp học của riêng bạn, sau đó bạn có thể register a PropertyChangedCallback khi bạn tạo DependencyProperty. Bạn có thể sử dụng điều này để nghe các thay đổi của thuộc tính.

Nếu bạn đang làm việc với một phân lớp, bạn có thể sử dụng OverrideMetadata để thêm PropertyChangedCallback của riêng bạn vào DP sẽ được gọi thay vì bất kỳ phiên bản gốc nào.

+9

Theo [MSDN] (http://msdn.microsoft.com/en-us/library/ms597491.aspx) và kinh nghiệm của tôi, các đặc tính _Some (của siêu dữ liệu được cung cấp) ... Những người khác, chẳng hạn như PropertyChangedCallback, được kết hợp ._ Vì vậy, PropertyChangedCallback của riêng bạn sẽ được gọi * ngoài * cho các cuộc gọi lại hiện tại, chứ không phải * thay vì *. –

+1

liên kết chết? là https://msdn.microsoft.com/library/ms745795%28v=vs.100%29.aspx ngay bây giờ? –

1

Nếu đúng như vậy, One hack. Bạn có thể giới thiệu một lớp tĩnh với 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.

129

Phương pháp này chắc chắn là mất tích ở đây:

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

Hãy rất cẩn thận với điều này vì nó có thể easliy giới thiệu rò rỉ bộ nhớ! Luôn loại bỏ trình xử lý một lần nữa bằng cách sử dụng 'descriptor.RemoveValueChanged (...)' – CodeMonkey

+7

xem chi tiết và phương pháp thay thế (xác định thuộc tính phụ thuộc mới + ràng buộc) tại http://agsmith.wordpress.com/2008/04/07/propertydescriptor -addvaluechanged-alternative/ – Lu55

+2

Điều này làm việc cho WPF (đó là câu hỏi này là gì). Nếu bạn hạ cánh ở đây tìm kiếm một giải pháp cửa sổ cửa hàng, bạn cần phải sử dụng thủ thuật ràng buộc. Tìm thấy bài đăng trên blog này có thể trợ giúp: http://blogs.msdn.com/b/flaviencharlon/archive/2012/12/07/getting-change-notifications-from-any-dependency-property-in-windows-store- apps.aspx Có lẽ cũng làm việc với WPF (như đã đề cập trong câu trả lời ở trên). – Gordon

14

tôi đã viết lớp tiện ích này:

  • Nó cung cấp cho DependencyPropertyChangedEventArgs với cũ & giá trị mới.
  • Nguồn được lưu trữ trong tham chiếu yếu trong liên kết.
  • Không chắc chắn nếu lộ Binding & BindingExpression là một ý tưởng hay.
  • Không bị rò rỉ.
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); 
    } 
} 
+0

nếu ràng buộc là OneWay, tại sao bạn thiết lập UpdateSourceTrigger? – Maslow

+0

@Maslow Không có lý do, chỉ là tiếng ồn, tôi sẽ cập nhật, cảm ơn. –

2

Bạn có thể kế thừa Control bạn đang cố gắng lắng nghe, và sau đó có thể truy cập trực tiếp đến công

protected void OnPropertyChanged(string name) 

Không có nguy cơ rò rỉ bộ nhớ.

Đừng sợ kỹ thuật OO tiêu chuẩn.

3

Có nhiều cách để đạt được điều này. Dưới đây là một cách để chuyển đổi một tài sản phụ thuộc vào một thể quan sát được, như vậy mà nó có thể được đăng ký 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)); 
     }); 
    } 
} 

Cách sử dụng

Hãy nhớ để vứt bỏ các mục đăng ký để ngăn chặn 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); 
    } 
} 
Các vấn đề liên quan