6

पर कॉल प्रेषित करने के लिए एक INotifyPropertyChanged प्रॉक्सी बनाना, मैं एक अलग (गैर-जीयूआई) थ्रेड द्वारा परिवर्तित वस्तुओं को WinForms नियंत्रण को बाध्य करने के लिए गतिशील प्रॉक्सी बनाना चाहता हूं। ऐसी प्रॉक्सी प्रॉपर्टी चेंजेड इवेंट को रोकती है और उचित सिंक्रनाइज़ेशन कॉन्टेक्स्ट का उपयोग करके इसे प्रेषित करती है।UI थ्रेड

इस तरह मैं नौकरी करने के लिए एक सहायक वर्ग का उपयोग कर सकता हूं, हर बार मैन्युअल रूप से सिंक्रनाइज़ेशन को लागू किए बिना (if (control.InvokeRequired) etc.)।

क्या लिनफू, कैसल या इसी तरह की लाइब्रेरी का उपयोग करने का कोई तरीका है?

[संपादित करें]

डेटा स्रोत जरूरी एक सूची नहीं है। यह किसी भी व्यापार वस्तु, जैसे हो सकता है:

interface IConnection : INotifyPropertyChanged 
{ 
    ConnectionStatus Status { get; } 
} 

मैं एक आवरण है जो काम कर सकता है बना सकते हैं, और यह कुछ इस तरह दिखेगा:

public class ConnectionWrapper : IConnection 
{ 
    private readonly SynchronizationContext _ctx; 
    private readonly IConnection _actual; 
    public ConnectionWrapper(IConnection actual) 
    { 
     _ctx = SynchronizationContext.Current; 
     _actual= actual; 
     _actual.PropertyChanged += 
      new PropertyChangedEventHandler(actual_PropertyChanged); 
    } 

    // we have to do 2 things: 
    // 1. wrap each property manually 
    // 2. handle the source event and fire it on the GUI thread 

    private void PropertyChanged(object sender, PropertyChangedEvArgs e) 
    { 
     // we will send the same event args to the GUI thread 
     _ctx.Send(delegate { this.PropertyChanged(sender, e); }, null); 
    } 

    public ConnectionStatus Status 
    { get { return _instance.Status; } } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

(इस में कुछ त्रुटियों हो सकता है कोड, मैं इसे बना रहा हूं)

मैं क्या करना चाहता हूं कि गतिशील प्रॉक्सी (प्रतिबिंब.इमिट) इस के लिए एक लाइनर है, उदाहरण के लिए

IConnection syncConnection 
     = new SyncPropertyChangedProxy<IConnection>(actualConnection); 

और मैं जानना चाहता था कि मौजूदा गतिशील प्रॉक्सी कार्यान्वयन का उपयोग करके ऐसा कुछ संभव था या नहीं।

एक और सामान्य प्रश्न होगा: एक गतिशील प्रॉक्सी बनाते समय किसी ईवेंट को कैसे रोकें? सभी कार्यान्वयन में इंटरसेप्टिंग (ओवरराइडिंग) गुणों को अच्छी तरह से समझाया गया है।

[EDIT2]

कारण (मुझे लगता है कि) मैं की जरूरत है एक प्रॉक्सी है कि स्टैक ट्रेस इस तरह दिखता है:

 
at PropertyManager.OnCurrentChanged(System.EventArgs e) 
at BindToObject.PropValueChanged(object sender, EventArgs e) 
at PropertyDescriptor.OnValueChanged(object component, EventArgs e) 
at ReflectPropertyDescriptor.OnValueChanged(object component, EventArgs e) 
at ReflectPropertyDescriptor.OnINotifyPropertyChanged(object component, 
    PropertyChangedEventArgs e)  
at MyObject.OnPropertyChanged(string propertyName) 

आप देख सकते हैं कि BindToObject.PropValueChangedsender उदाहरण पास नहीं PropertyManager पर, और परावर्तक दिखाता है कि प्रेषक ऑब्जेक्ट को कहीं भी संदर्भित नहीं किया गया है। दूसरे शब्दों में, जब PropertyChanged ईवेंट ट्रिगर किया जाता है, तो घटक मूल (बाध्य) डेटा स्रोत की संपत्ति तक पहुंचने के लिए प्रतिबिंब का उपयोग करेगा।

यदि मैंने अपनी ऑब्जेक्ट को केवल उस ईवेंट (जैसे Sam प्रस्तावित) श्रेणी में लपेट लिया है, तो ऐसे रैपर वर्ग में कोई भी गुण नहीं होगा जिसे प्रतिबिंब के माध्यम से एक्सेस किया जा सके।

+0

'देखें ThreadedBindingList' - इसे यहाँ इतने पर दोहराया गया है (http://stackoverflow.com/questions/455766/how-do-you -correctly अद्यतन एक डेटाबाउंड-DataGridView-से-एक पृष्ठभूमि धागा)। –

उत्तर

4

यहां एक ऐसी कक्षा है जो एक INotifyPropertyChanged को लपेट लेगी, सिंक्रनाइज़ेशन कॉन्टेक्स्ट.क्यूरेंट के माध्यम से PropertyChanged ईवेंट को आगे बढ़ाएगी, और संपत्ति को अग्रेषित करेगी।

इस समाधान को काम करना चाहिए, लेकिन कुछ समय के साथ इसे संपत्ति नाम के बजाय लैम्ब्डा अभिव्यक्ति का उपयोग करने के लिए बेहतर किया जा सकता है। इससे प्रतिबिंब से छुटकारा पाने की अनुमति मिल जाएगी, संपत्ति को टाइप की गई पहुंच प्रदान की जाएगी। इसके साथ जटिलता आपको संपत्ति नाम खींचने के लिए लैम्ब्डा से अभिव्यक्ति वृक्ष भी प्राप्त करने की आवश्यकता है ताकि आप इसे ऑनसोर्सप्रॉपर्टी चेंज विधि में उपयोग कर सकें। मैंने एक लैम्ब्डा अभिव्यक्ति पेड़ से संपत्ति का नाम खींचने के बारे में एक पोस्ट देखी लेकिन मुझे अभी यह नहीं मिला।

इस वर्ग का उपयोग करने के लिए, आप अपने इस तरह बाध्यकारी बदलना चाहते हैं:

Bindings.Add("TargetProperty", new SyncBindingWrapper<PropertyType>(source, "SourceProperty"), "Value"); 

और यहाँ है SyncBindingWrapper:

using System.ComponentModel; 
using System.Reflection; 
using System.Threading; 

public class SyncBindingWrapper<T> : INotifyPropertyChanged 
{ 
    private readonly INotifyPropertyChanged _source; 
    private readonly PropertyInfo _property; 

    public event PropertyChangedEventHandler PropertyChanged; 

    public T Value 
    { 
     get 
     { 
      return (T)_property.GetValue(_source, null); 
     } 
    } 

    public SyncBindingWrapper(INotifyPropertyChanged source, string propertyName) 
    { 
     _source = source; 
     _property = source.GetType().GetProperty(propertyName); 
     source.PropertyChanged += OnSourcePropertyChanged; 
    } 

    private void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName != _property.Name) 
     { 
      return; 
     } 
     PropertyChangedEventHandler propertyChanged = PropertyChanged; 
     if (propertyChanged == null) 
     { 
      return; 
     } 

     SynchronizationContext.Current.Send(state => propertyChanged(this, e), null); 
    } 
} 
+1

धन्यवाद, यह मूल रूप से मैंने अंत में किया था, इसे स्वीकार करना भूल गया। जो चीज मुझे परेशान करती थी वह यह थी कि मैंने सोचा कि मुझे किसी ऑब्जेक्ट के लिए एक ही रैपर चाहिए, जबकि वास्तव में मुझे इसे बनाने के लिए प्रत्येक * संपत्ति * को एक अलग रैपर में लपेटने की आवश्यकता होती है। – Groo

+0

यह बस शानदार है! –

+0

बस यह पाया, ठीक वही जो मैं खोज रहा था। एक बात यह है कि, किसी और के लिए जो आता है, वह है: सिंक बाइंडिंगवापर को स्रोत ऑब्जेक्ट की प्रॉपर्टी चेंजेड इवेंट से खुद को हटाने का साधन प्रदान करना चाहिए, शायद आईडीस्पोजेबल को लागू करके। – SimonC

2

मैं एक ही समस्या है भर में आ गए हैं और शमूएल के समाधान नहीं था ' टी मेरे लिए काम नहीं करता है, इसलिए मैंने कन्स्ट्रक्टर में सिंक्रनाइज़ेशन संदर्भ प्रारंभिकरण रखा है, और "Value" संपत्ति का नाम मूल संपत्ति के बजाय पास किया जाना चाहिए। यह मेरे लिए काम किया:

public class SyncBindingWrapper: INotifyPropertyChanged 
{ 
    private readonly INotifyPropertyChanged _source; 
    private readonly PropertyInfo _property; 

    public event PropertyChangedEventHandler PropertyChanged; 

    private readonly SynchronizationContext _context; 

    public object Value 
    { 
     get 
     { 
      return _property.GetValue(_source, null); 
     } 
    } 

    public SyncBindingWrapper(INotifyPropertyChanged source, string propertyName) 
    { 
     _context = SynchronizationContext.Current; 
     _source = source; 
     _property = source.GetType().GetProperty(propertyName); 
     source.PropertyChanged += OnSourcePropertyChanged; 
    } 

    private void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     var propertyChanged = PropertyChanged; 
     if (propertyChanged != null && e.PropertyName == _property.Name) 
     { 
      _context.Send(state => propertyChanged(this, new PropertyChangedEventArgs("Value")), null); 
     } 
    } 
} 

उपयोग:

_textBox1.DataBindings.Add("Text", new SyncBindingWrapper(someObject, "SomeProperty"), "Value"); 
+0

हां, धन्यवाद, आईआईआरसी मैंने विधि को उसी तरीके से भी तय किया लेकिन अपडेट करना भूल गया। 'ऑनसोर्सप्रॉपर्टी चेंज' में 'सिंक्रनाइज़ेशन कॉन्टेक्स्ट.कुरेंट' का उपयोग करना समझ में नहीं आता है। – Groo

संबंधित मुद्दे