निर्भरता संपत्ति के परिवर्तनों को सुनो


80

क्या ए के परिवर्तनों को सुनने का कोई तरीका है DependencyProperty? जब मूल्य में बदलाव होता है तो मैं अधिसूचित होना चाहता हूं और कुछ क्रियाएं करता हूं लेकिन मैं बाध्यकारी का उपयोग नहीं कर सकता। यह DependencyPropertyदूसरे वर्ग का है।


आप क्यों कहते हैं कि आप बाध्यकारी का उपयोग नहीं कर सकते हैं?
रॉबर्ट रॉसनी

जवाबों:


59

यदि यह DependencyPropertyएक अलग वर्ग का है, तो सबसे आसान तरीका यह है कि किसी मान को बाँध दिया जाए, और उस मूल्य पर बदलावों को सुनें।

यदि DP वह है जिसे आप अपने स्वयं के वर्ग में लागू कर रहे हैं, तो आप जब आप एक प्रॉपर्टीचैडबैक को पंजीकृत कर सकते हैं DependencyProperty। आप संपत्ति के परिवर्तनों को सुनने के लिए इसका उपयोग कर सकते हैं।

यदि आप एक उपवर्ग के साथ काम कर रहे हैं, तो आप डीपी को जोड़ने के लिए ओवरराइडमेटेटाटा का उपयोग कर सकते हैं PropertyChangedCallbackजो कि किसी भी मूल के बजाय कॉल किया जाएगा।


11
MSDN और मेरे अनुभव के अनुसार , कुछ विशेषताओं (आपूर्ति किए गए मेटाडेटा के) ... अन्य, जैसे कि PropertyChangedCallback, संयुक्त हैं। तो अपने खुद के PropertyChangedCallback मौजूदा कॉलबैक के अलावा बुलाया जाएगा , इसके बजाय
मार्सेल गोसलिन

1
मृत लिंक? क्या यह अब msdn.microsoft.com/library/ms745795%28v=vs.100%29.aspx है?
साइमन के।

क्या इस स्पष्टीकरण को उत्तर में जोड़ना संभव होगा? मुझे लगा कि ओवरराइडमेटेटा माता-पिता के कॉलबैक को बदल देगा, और यह मुझे इसका उपयोग करने से रोक रहा था।
उपयोगकर्ता नाम

1
मैं मानता हूं, यह बहुत स्पष्ट नहीं है: "सबसे आसान तरीका यह है कि एक मूल्य को बाँध दिया जाए, और उस मूल्य पर परिवर्तन को सुनें"। एक उदाहरण बहुत मददगार होगा
UUDdLrLrSs

154

यह विधि निश्चित रूप से यहाँ गायब है:

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

67
इस के साथ बहुत ही उपयोगी हो क्योंकि यह आसानी से स्मृति लीक शुरू कर सकते हैं! हमेशा एक हैंडलर का उपयोग करके फिर से निकालेंdescriptor.RemoveValueChanged(...)
कोडमोंकी

7
विवरण देखें और agsmith.wordpress.com/2008/04/07/…
Lu55

2
यह WPF के लिए काम करता है (जो कि यह प्रश्न किस लिए है)। यदि आप एक विंडोज़ स्टोर समाधान की तलाश में यहां उतरते हैं, तो आपको बाध्यकारी चाल का उपयोग करने की आवश्यकता है। यह ब्लॉग पोस्ट मिली जो मदद कर सकती है: blogs.msdn.com/b/flaviencharlon/archive/2012/12/07/… संभवतः WPF के साथ भी काम करता है (जैसा कि ऊपर दिए गए जवाब में बताया गया है)।
गॉर्डन

2
@ टोड: मुझे लगता है कि रिसाव के आसपास एक और तरीका है, हैंडलर के संदर्भ के कारण दृश्य आपके दृश्य-मॉडल को जीवित रख सकता है। जब दृश्य डिस्पोज़ हो रहा हो तो सदस्यता वैसे भी गायब हो जानी चाहिए। लोग इवेंट हैंडलर से लीक के बारे में थोड़ा बहुत पागल हैं, मुझे लगता है कि आमतौर पर यह कोई मुद्दा नहीं है।
एचबी

4
@HB इस मामले DependencyPropertyDescriptorमें आवेदन में सभी हैंडलर की स्थिर सूची है, इसलिए हैंडलर में संदर्भित प्रत्येक वस्तु लीक हो जाएगी। यह आम घटना की तरह काम नहीं करता है।
ghord

19

मैंने यह उपयोगिता वर्ग लिखा है:

  • यह पुराने और नए मूल्य के साथ DependencyPropertyChangedEventArgs देता है।
  • स्रोत को बंधन में कमजोर संदर्भ में संग्रहीत किया जाता है।
  • निश्चित नहीं है कि अगर बाइंडिंग और बाइंडिंग एक्सप्रेशन को उजागर करना एक अच्छा विचार है।
  • कोई लीक नहीं।
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);
    }
}

यदि बंधन OneWay है, तो आप UpdateSourceTrigger क्यों सेट कर रहे हैं?
मैस्लो

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));
        });
    }
}

प्रयोग

स्मृति लीक को रोकने के लिए सदस्यता को निपटाना याद रखें:

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);
    }
}

4

आप उस नियंत्रण को प्राप्त कर सकते हैं जिसे आप सुनने की कोशिश कर रहे हैं, और उसके बाद सीधे पहुंच है:

protected void OnPropertyChanged(string name)

मेमोरी लीक का कोई खतरा नहीं

मानक OO तकनीकों से डरो मत।


1

अगर ऐसा है तो वन हैक। आप एक के साथ एक स्थिर वर्ग का परिचय दे सकते हैं DependencyProperty। आप स्रोत वर्ग भी उस डीपी से बंध जाते हैं और आपका गंतव्य वर्ग भी डीपी से जुड़ जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.