2012-04-14 19 views
5

का कारण बनता है यहाँ मेरी scenarion है: मैं एक GridControl एक BindingList करने के लिए बाध्य हैINotifyPropertyChanged पार धागा त्रुटि

। मैं पहली बार क्या कर रहा था एक कार्यकर्ता धागा बनाने और सीधे BindingList का उपयोग किया गया था, लेकिन यह एक "क्रॉस-धागा आपरेशन का पता चला" फेंक, इसलिए किया गया था पर मैं गाइड यहाँ का पालन किया:

http://www.devexpress.com/Support/Center/p/AK2981.aspx

मूल क्लोनिंग करके कार्यकर्ता धागे में बाध्यकारी सूची और उसे बदलकर, मुझे वांछित प्रभाव मिला। हालांकि, मैंने हाल ही में INOTifyProperty को लागू किया है जो बाइंडिंगलिस्ट में आयोजित ऑब्जेक्ट में बदल गया है, और मुझे फिर से त्रुटि मिलनी शुरू हुई।

मेरा अनुमान है कि ग्रिड व्यू अभी भी वस्तु से बदलकर INotifyProperty को सुन रहा है।

मैं इसे कैसे ठीक कर सकता हूं?

मेरी कक्षा:

public class Proxy : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string name) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 

उत्तर

10

आप यूआई धागा (इस तरह के एक कार्यकर्ता धागे से के रूप में) के बाहर से यूआई जोड़ तोड़ कर रहे हैं, तो आप यूआई धागा में पुन: शामिल करने की जरूरत है। आप UI नियंत्रण पर Invoke पर कॉल करके ऐसा कर सकते हैं। यदि आप InvokeRequired का उपयोग करके यह आवश्यक है तो आप परीक्षण कर सकते हैं।

पैटर्न आम तौर पर प्रयोग किया जाता है यह है:

public void ChangeText(string text) 
{ 
    if(this.InvokeRequired) 
    { 
     this.Invoke(new Action(() => ChangeText(text))); 
    } 
    else 
    { 
     label.Text = text; 
    } 
} 

आपके मामले में यूआई, INotifyPropertyChanged की वजह से चालाकी से किया जा रहा है ताकि आप यह सुनिश्चित करें कि या तो आप हमेशा यूआई धागे पर अपने इकाई को बदलना (बनाने की जरूरत है उपर्युक्त तकनीक का उपयोग करके), या generic asynchronous INotifyPropertyChanged helper का उपयोग करें। यह बाध्य होने वाली वस्तु के चारों ओर एक रैपर है। यह UI12 थ्रेड पर ChangeProperty ईवेंट आग को सुनिश्चित करने के लिए उपर्युक्त तकनीक का उपयोग करता है।

यहाँ एक Entity वर्ग के लिए एक प्रॉक्सी का एक बहुत ही कच्चे उदाहरण है। यह सुनिश्चित करता है कि संपत्ति परिवर्तन घटना यूआई थ्रेड को फिर से जोड़ती है, और इकाई को स्वयं को अनमोडिफाइड रखती है। जाहिर है, उदाहरण के लिए आप शायद अधिक सामान्य रूप से डायनामिक ऑब्जेक्ट का उपयोग करके इसे लागू करना चाहते हैं।

public class NotificationHelper : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    private readonly ISynchronizeInvoke invokeDelegate; 
    private readonly Entity entity; 

    public NotificationHelper(ISynchronizeInvoke invokeDelegate, Entity entity) 
    { 
     this.invokeDelegate = invokeDelegate; 
     this.entity = entity; 

     entity.PropertyChanged += OnPropertyChanged; 
    } 

    public string Name 
    { 
     get { return entity.Name; } 
    } 

    private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (PropertyChanged != null) 
     { 
      if (invokeDelegate.InvokeRequired) 
      { 
       invokeDelegate.Invoke(new PropertyChangedEventHandler(OnPropertyChanged), 
            new[] { sender, e }); 
       return; 
      } 
      PropertyChanged(this, e); 
     } 
    } 
} 
+0

हम्म .... तो मुझे इसे INotifyPropertyChanged ईवेंट में रखने की आवश्यकता है? मैंने अपने क्लास कोड के साथ सवाल अपडेट किया है। – TheGateKeeper

+0

मैंने स्पष्टीकरण के लिए अद्यतन किया है, या तो केवल यूआई थ्रेड पर बाध्य वस्तु को बदलें, या बाध्यकारी होने पर इसे एक सहायक वर्ग में लपेटें। – TheCodeKing

+0

इसका उपयोग नहीं किया, लेकिन इसे उत्तर के रूप में चिह्नित किया क्योंकि यह बहुत अधिक जानकारी प्रदान करता है। – TheGateKeeper

1

शायद ज़रुरत पड़े कोई एक ही समस्या में पड़ गया है ... मैं कुछ घंटों के बाद इसे ठीक करने में कामयाब रहे। यहां मैंने यह किया है:

असल में समस्या यह थी कि आईओटीआईटीप्रॉपर्टी चेंजड लागू करने वाली वस्तु एक कार्यकर्ता धागे में रह रही थी, और यूआई थ्रेड तक पहुंचने पर यह समस्याएं पैदा करती है।

तो क्या मैंने किया था वस्तु INotifyPropertyChanged वस्तु को अपडेट करने की आवश्यकता है कि के लिए एक संदर्भ गुजरती हैं, और फिर उस पर आह्वान का उपयोग किया गया था।

यहाँ कि यह दिखता है:

public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string name) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      //If the Proxy object is living in a non-UI thread, use invoke 
      if (c != null) 
      { 
       c.BeginInvoke(new Action(() => handler(this, new PropertyChangedEventArgs(name)))); 
      } 
      //Otherwise update directly 
      else 
      { 
       handler(this, new PropertyChangedEventArgs(name)); 
      } 

     } 
    } 

    //Use this to reference the object on the UI thread when there is need to 
    public Control C 
    { 
     set { c = value; } 
    } 

धागा से, सब मैंने किया था:

    prox.c = this; 
        //Logic here 
        prox.c = null; 

आशा इस कोई मदद करता है !!

+2

यह थोड़ा गंदा है क्योंकि इसका मतलब है कि आपके व्यावसायिक ऑब्जेक्ट में UI के संदर्भ हैं। एक रैपर वर्ग का उपयोग करने के लिए बेहतर, दूसरे उत्तर में लिंक देखें। – TheCodeKing

+0

अपडेट के लिए धन्यवाद, लेकिन मुझे लगता है कि आपको अभी भी ऑब्जेक्ट को एक तरफ या दूसरे संदर्भ में पास करने की आवश्यकता है। 'this.Invoke (नई क्रिया (() => चेंजटेक्स्ट (टेक्स्ट))); उदाहरण के लिए, वास्तविक नियंत्रण होने के लिए 'यह' की आवश्यकता होगी। – TheGateKeeper

+0

मैं थ्रेडिंग के लिए बहुत नया हूं इसलिए यह मेरे लिए बहुत नया है, मैं आपके द्वारा लिंक किए गए कोड को भी समझ नहीं सकता। मुझे लगता है कि मैं अब के लिए अपने रास्ते पर रहूंगा। – TheGateKeeper

2

मैं TheGateKeeper के अंतिम समाधान के लिए एक समान दृष्टिकोण लिया। हालांकि मैं कई अलग अलग वस्तुओं के लिए बाध्य किया गया था। तो मुझे थोड़ा और सामान्य कुछ चाहिए।समाधान एक रैपर बनाने के लिए था जो ICustomTypeDescriptor भी लागू किया गया था। इस तरह, मुझे UI में प्रदर्शित किए जा सकने वाले सभी चीज़ों के लिए रैपर गुण बनाने की आवश्यकता नहीं है।

private void CreateBindings() 
    { 
     if (_model == null) return; 

     var threadSafeModel = new SynchronizedNotifyPropertyChanged<MyViewModel>(_model, this); 

     directiveLabel.DataBindings.Add("Text", threadSafeModel, "DirectiveText", false, DataSourceUpdateMode.OnPropertyChanged); 
    } 

MyViewModel एक "DirectiveText" संपत्ति है और कोई विशेष विचार सूत्रण के साथ या दृश्य वर्गों के लिए INotifyPropertyChanged लागू करता है:

public class SynchronizedNotifyPropertyChanged<T> : INotifyPropertyChanged, ICustomTypeDescriptor 
    where T : INotifyPropertyChanged 
{ 
    private readonly T _source; 
    private readonly ISynchronizeInvoke _syncObject; 

    public SynchronizedNotifyPropertyChanged(T source, ISynchronizeInvoke syncObject) 
    { 
     _source = source; 
     _syncObject = syncObject; 

     _source.PropertyChanged += (sender, args) => OnPropertyChanged(args.PropertyName); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged == null) return; 

     var handler = PropertyChanged; 
     _syncObject.BeginInvoke(handler, new object[] { this, new PropertyChangedEventArgs(propertyName) }); 
    } 

    public T Source { get { return _source; }} 

    #region ICustomTypeDescriptor 
    public AttributeCollection GetAttributes() 
    { 
     return new AttributeCollection(null); 
    } 

    public string GetClassName() 
    { 
     return TypeDescriptor.GetClassName(typeof(T)); 
    } 

    public string GetComponentName() 
    { 
     return TypeDescriptor.GetComponentName(typeof (T)); 
    } 

    public TypeConverter GetConverter() 
    { 
     return TypeDescriptor.GetConverter(typeof (T)); 
    } 

    public EventDescriptor GetDefaultEvent() 
    { 
     return TypeDescriptor.GetDefaultEvent(typeof (T)); 
    } 

    public PropertyDescriptor GetDefaultProperty() 
    { 
     return TypeDescriptor.GetDefaultProperty(typeof(T)); 
    } 

    public object GetEditor(Type editorBaseType) 
    { 
     return TypeDescriptor.GetEditor(typeof (T), editorBaseType); 
    } 

    public EventDescriptorCollection GetEvents() 
    { 
     return TypeDescriptor.GetEvents(typeof(T)); 
    } 

    public EventDescriptorCollection GetEvents(Attribute[] attributes) 
    { 
     return TypeDescriptor.GetEvents(typeof (T), attributes); 
    } 

    public PropertyDescriptorCollection GetProperties() 
    { 
     return TypeDescriptor.GetProperties(typeof (T)); 
    } 

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
    { 
     return TypeDescriptor.GetProperties(typeof(T), attributes); 
    } 

    public object GetPropertyOwner(PropertyDescriptor pd) 
    { 
     return _source; 
    } 
    #endregion ICustomTypeDescriptor 
} 

फिर Ui में, मैं इस आवरण को की तरह कुछ का उपयोग कर बाँध।

0

मैंने BindingList उपclassed तो मैं एक आवश्यक Invoke की जांच कर सकता था। इस तरह से मेरे व्यावसायिक वस्तुओं में यूआई का संदर्भ नहीं है।

public class InvokingBindingList<T> : BindingList<T> 
{ 
    public InvokingBindingList(IList<T> list, Control control = null) : base(list) 
    { 
    this.Control = control; 
    } 

    public InvokingBindingList(Control control = null) 
    { 
    this.Control = control; 
    } 

    public Control Control { get; set; } 

    protected override void OnListChanged(ListChangedEventArgs e) 
    { 
    if (Control?.InvokeRequired == true) 
     Control.Invoke(new Action(() => base.OnListChanged(e))); 
    else 
     base.OnListChanged(e); 
    } 
} 
संबंधित मुद्दे