2011-01-21 4 views
62

Есть ли способ прослушать изменения DependencyProperty? Я хочу получать уведомления и выполнять некоторые действия при изменении значения, но я не могу использовать привязку. Это DependencyProperty другого класса.Прослушать изменения свойства зависимостей

+0

Почему вы говорите, что не можете использовать привязку? –

ответ

49

Если это DependencyProperty отдельного класса, самым простым способом является привязка к нему значения и прослушивание изменений этого значения.

Если DP - это тот, который вы реализуете в своем классе, то вы можете register a PropertyChangedCallback при создании DependencyProperty. Вы можете использовать это, чтобы прослушать изменения свойства.

Если вы работаете с подклассом, вы можете использовать OverrideMetadata, чтобы добавить свой собственный PropertyChangedCallback в DP, который будет вызываться вместо любого оригинального.

+9

В соответствии с [MSDN] (http://msdn.microsoft.com/en-us/library/ms597491.aspx) и моим опытом, некоторые характеристики (из поставляемых метаданных) ... Другие, такие как PropertyChangedCallback, объединены . Таким образом, ваш собственный PropertyChangedCallback будет получать * дополнительно * к существующим обратным вызовам, а не * вместо *. –

+1

мертвая ссылка? это https://msdn.microsoft.com/library/ms745795%28v=vs.100%29.aspx сейчас? –

1

Если это так, один взлом. Вы можете ввести класс Static с DependencyProperty. Класс source также привязывается к этому dp, и ваш целевой класс также привязывается к DP.

129

Этот метод, безусловно, здесь отсутствует:

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

Будьте очень осторожны с этим, так как он может easliy ввести утечки памяти! Всегда удаляйте обработчик снова с помощью 'descriptor.RemoveValueChanged (...)' – CodeMonkey

+7

см. Подробности и альтернативный подход (определение нового свойства зависимостей + привязка) по адресу http://agsmith.wordpress.com/2008/04/07/propertydescriptor -addvaluechanged-alternative/ – Lu55

+2

Это работает для WPF (для чего этот вопрос нужен). Если вы приземляетесь здесь в поисках решения для магазина Windows, вам нужно использовать трюк привязки. Нашли это сообщение в блоге, которое могло бы помочь: http://blogs.msdn.com/b/flaviencharlon/archive/2012/12/07/getting-change-notifications-from-any-dependency-property-in-windows-store- apps.aspx Вероятно, также работает с WPF (как упоминалось в вышеприведенном ответе). – Gordon

14

Я написал эту утилиту класс:

  • Это дает DependencyPropertyChangedEventArgs со старым & новым значением.
  • Источник хранится в слабой ссылке в привязке.
  • Не уверен, что выявление привязки & 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); 
    } 
} 
+0

, если привязка OneWay, почему вы устанавливаете UpdateSourceTrigger? – Maslow

+0

@Maslow Нет причин, просто шум, я обновлю, спасибо. –

2

Вы могли наследовать управления вы пытаетесь слушать, а затем иметь прямой доступ к:

protected void OnPropertyChanged(string name) 

Нет риска утечки памяти.

Не бойтесь стандартных методов ОО.

3

Существует несколько способов достижения этого. Вот способ, чтобы преобразовать зависимое свойство наблюдаемого, таким образом, что он может быть подписан с использованием 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)); 
     }); 
    } 
} 

Использования

Не забудьте расположить подписки для предотвращения утечек памяти:

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); 
    } 
} 
Смежные вопросы