聆听依赖属性的变化

Ras*_*sto 74 .net c# wpf events dependency-properties

有没有办法听一个变化DependencyProperty?我希望在值更改时收到通知并执行一些操作但我不能使用绑定.这是DependencyProperty另一个班级.

H.B*_*.B. 146

这个方法肯定在这里丢失:

DependencyPropertyDescriptor
    .FromProperty(RadioButton.IsCheckedProperty, typeof(RadioButton))
    .AddValueChanged(radioButton, (s,e) => { /* ... */ });
Run Code Online (Sandbox Code Playgroud)

  • 要非常小心,因为它很容易引入内存泄漏!始终使用`descriptor.RemoveValueChanged(...)`再次删除处理程序 (62认同)
  • 在http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/上查看详细信息和另一种方法(定义新的依赖属性+绑定) (7认同)
  • @HB在这种情况下,`DependencyPropertyDescriptor`具有应用程序中所有处理程序的静态列表,因此处理程序中引用的每个对象都将泄漏.它不像普通事件那样工作. (4认同)
  • @Todd:我认为泄漏是相反的,由于对处理程序的引用,视图可能会使您的视图模型保持活动状态。当视图正在处理时,订阅也应该消失。我认为人们对事件处理程序的泄漏有点过于偏执,通常这不是问题。 (3认同)
  • 这适用于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(如上面的答案所述). (2认同)

Ree*_*sey 56

如果它DependencyProperty是一个单独的类,最简单的方法是将值绑定到它,并监听该值的更改.

如果DP是您在自己的类中实现的,那么您可以在创建时注册PropertyChangedCallbackDependencyProperty.您可以使用它来收听属性的更改.

如果您正在使用子类,则可以使用OverrideMetadata将您自己的内容添加PropertyChangedCallback到将被调用的DP而不是任何原始的DP.

  • 根据[MSDN](http://msdn.microsoft.com/en-us/library/ms597491.aspx)和我的经验,_Some特性(提供的元数据)......其他的,如PropertyChangedCallback,结合起来. _所以你自己的PropertyChangedCallback将被调用*加上*现有的回调,而不是*而不是*. (10认同)
  • 我同意,这不是很清楚:“最简单的方法是将一个值绑定到它,并监听该值的变化”。一个例子会很有帮助 (2认同)

Joh*_*son 18

我写了这个实用程序类:

  • 它为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);
    }
}
Run Code Online (Sandbox Code Playgroud)
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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Tod*_*odd 6

您可以继承您尝试监听的 Control,然后直接访问:

protected void OnPropertyChanged(string name)
Run Code Online (Sandbox Code Playgroud)

没有内存泄漏的风险。

不要害怕标准的面向对象技术。


Mov*_*GP0 6

有多种方法可以实现这一目标.这是一种将依赖属性转换为可观察对象的方法,以便可以使用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));
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

请记住处置订阅以防止内存泄漏:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)