2009-12-10 14 views
6

साथ INotifyPropertyChanged मैं एकधागे

BindingList<T> 

जो एक DataGridView करने के लिए बाध्य कर रहा है। मेरी कक्षा में एक संपत्ति की गणना करने में काफी समय लगता है, इसलिए मैंने कार्रवाई को थ्रेड किया। गणना के बाद मैं ऑनप्रॉपर्टी चेंज() घटना को ग्रिड को सूचित करने के लिए बढ़ाता हूं कि मूल्य तैयार है।

कम से कम, यह सिद्धांत है। लेकिन चूंकि OnPropertyChanged विधि को भिन्नता धागे से बुलाया जाता है, इसलिए मुझे ग्रिड की OnRowPrePaint विधि में कुछ थके हुए अपवाद मिलते हैं।

क्या कोई मुझे बता सकता है कि मैं मुख्य थ्रेड में ऑनप्रॉपर्टी चेंज किए गए ईवेंट को कैसे निकाला जा सकता हूं? मैं Form.Invoke का उपयोग नहीं कर सकता, क्योंकि कक्षा MyClass को पता नहीं है कि यह Winforms एप्लिकेशन में चलता है।

public class MyClass : INotifyPropertyChanged 
{ 
    public int FastMember {get;set;} 

    private int? slowMember; 
    public SlowMember 
    { 
     get 
     { 
      if (slowMember.HasValue) 
       return slowMember.Value; 
      else 
      { 
       Thread t = new Thread(getSlowMember); 
       t.Start(); 
       return -1; 
      } 

     } 
    } 

    private void getSlowMember() 
    { 
     Thread.Sleep(1000); 
     slowMember = 5; 
     OnPropertyChanged("SlowMember"); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string propertyName) 
    { 
     PropertyChangingEventHandler eh = PropertyChanging; 
     if (eh != null) 
     { 
      eh(this, e); 
     } 
    } 

} 

उत्तर

8

डिज़ाइन द्वारा, नियंत्रण केवल उस धागे द्वारा अपडेट किया जा सकता है जिसे बनाया गया था। यही कारण है कि आपको अपवाद मिल रहे हैं।

BackgroundWorker का उपयोग करने पर विचार करें और केवल लंबे समय तक चलने वाले ऑपरेशन के बाद सदस्य को अद्यतन करें, एक ईवेंटशेलर को RunWorkerCompleted पर सब्सक्राइब करके पूरा कर लिया है।

+0

एक आकर्षण की तरह काम करता है। अब तक मुझे पृष्ठभूमिवर्कर के बारे में पता नहीं था। यह इस काम को इतना आसान बनाता है, बहुत कुछ। –

1

विचार 1:
इस लेख में UIThreadMarshal वर्ग पर एक नज़र और इसके उपयोग लें:
UI Thread Marshaling in the Model Layer
आप उदाहरण के लिए स्थिर से वर्ग बदल सकते हैं और अपने वस्तु को इसकी सुई कर सकते हैं। तो आपकी वस्तु फॉर्म क्लास के बारे में नहीं जान पाएगी। यह केवल यूआईटीएच ट्रेडमार्क कक्षा के बारे में पता चलेगा।

विचार 2:
मुझे नहीं लगता कि आपकी संपत्ति से वापसी -1 अच्छा विचार है। यह मेरे लिए एक खराब डिजाइन की तरह दिखता है।

विचार 3:
शायद आपकी कक्षा को एथर थ्रेड का उपयोग नहीं करना चाहिए। हो सकता है कि यह उपभोक्ता वर्ग है जो निर्णय लेना चाहिए कि अपनी संपत्ति को कैसे कॉल करें: सीधे या अलग धागे में। इस मामले में आपको अतिरिक्त संपत्ति प्रदान करने की आवश्यकता है, जैसे IsSlowMemberInitialized।

+0

से 1: लिंक के लिए धन्यवाद। पृष्ठभूमिवर्कर ने इस मामले में मेरी समस्या हल की लेकिन मैं अपने शॉर्ट्स पर शर्त लगाता हूं कि मुझे निकट भविष्य में इसकी आवश्यकता होगी। टू 2: आप सही हैं, विशेष रूप से बीकॉज़ स्लोमम्बर -1 हो सकता है। संभव नहीं है, DataGridView beacause मूल्य प्रश्नों (और हो जाता है -1 पहली बार के लिए, की तुलना में मैं मान को अद्यतन और INotifyPropertyChanged इंटरफ़ेस का उपयोग बदली हुई संपत्ति, के लिए है जो की DataGridView सूचित करने के लिए: बस परीक्षण 3 करने के लिए के लिए दिया गया है मुख्य थ्रेड में होती हैं। (ठीक है, मैं एक टाइमर का उपयोग करें और IsSlowMemberInitialized = सच के लिए जांच कर सकता है लेकिन यह है कि बदसूरत है। वैसे भी एक बहुत thx। –

+0

आप DataGridView का उपयोग करते हैं, तो शायद आप BindingSource का उपयोग करने की जरूरत है। लिंक मैं तुम्हें दे दिया है, वहाँ एक BindingSource जो अलग धागे से बाध्यकारी का समर्थन करता है के कार्यान्वयन है। आपको लगता है कि कोड पर काम कर सकते बेहतर अपनी आवश्यकताओं के लिए उपयुक्त बनाने के लिए। – nightcoder

2

यहां कुछ ऐसा है जो मैंने कुछ समय पहले लिखा था; यह समुचित रूप से काम करना चाहिए, लेकिन अद्यतन की बहुत सारी की लागत ध्यान दें ...

using System.ComponentModel; 
using System.Threading; 
public class ThreadedBindingList<T> : BindingList<T> { 
    SynchronizationContext ctx = SynchronizationContext.Current; 
    protected override void OnAddingNew(AddingNewEventArgs e) { 
     if (ctx == null) { BaseAddingNew(e); } 
     else { ctx.Send(delegate { BaseAddingNew(e); }, null); } 
    } 
    protected override void OnListChanged(ListChangedEventArgs e) { 
     if (ctx == null) { BaseListChanged(e); } 
     else { ctx.Send(delegate { BaseListChanged(e); }, null); } 
    } 
    void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); } 
    void BaseAddingNew(AddingNewEventArgs e) { base.OnAddingNew(e); } 
} 
+0

दिलचस्प कार्यान्वयन मार्क, लेकिन जैसा कि यह बुरा डिजाइन के लिए अनुमति देता है मैं इसे केवल कुछ परिदृश्य के दशक में इस्तेमाल किया जाना चाहिए लगता है जहां कार्रवाई को संसाधित करते समय आपको वास्तव में अपडेट करने के लिए नियंत्रण की आवश्यकता होती है। –

6

लोग कभी कभी, भूल जाते हैं कि ईवेंट हैंडलर एक MultiCastDelegate है और इस तरह के रूप में, प्रत्येक ग्राहक के बारे में सभी जानकारी है कि हम Invoke + सिंक्रनाइज़ेशन प्रदर्शन जुर्माना अनावश्यक रूप से लागू किए बिना इस स्थिति को गहन रूप से संभालने की आवश्यकता है। मैं उम्र के लिए इस तरह कोड का उपयोग किया गया है:

using System.ComponentModel; 
// ... 

public event PropertyChangedEventHandler PropertyChanged; 

protected virtual void OnPropertyChanged(string propertyName) 
{ 
    var handler = PropertyChanged; 
    if (handler != null) 
    { 
     var e = new PropertyChangedEventArgs(propertyName); 
     foreach (EventHandler h in handler.GetInvocationList()) 
     { 
      var synch = h.Target as ISynchronizeInvoke; 
      if (synch != null && synch.InvokeRequired) 
       synch.Invoke(h, new object[] { this, e }); 
      else 
       h(this, e); 
     } 
    } 
} 

यह क्या करता है सरल है, लेकिन मुझे याद है कि मैं लगभग वापस मेरे मस्तिष्क फटा तो सबसे अच्छा तरीका यह करने के लिए खोजने की कोशिश।

यह किसी भी दौड़ की स्थिति से बचने के लिए स्थानीय संपत्ति पर ईवेंट हैंडलर को "पकड़ता है"।

यदि हैंडलर शून्य नहीं है (लीज पर एक ग्राहक मौजूद है) यह घटना तर्क तैयार करता है, और फिर इस मल्टीकास्ट प्रतिनिधि की आमंत्रण सूची के माध्यम से पुनरावृत्त करता है।

आमंत्रण सूची में लक्षित संपत्ति है, जो ईवेंट का ग्राहक है।यदि यह ग्राहक ISynchronizeInvoke लागू करता है (सभी यूआई नियंत्रण इसे कार्यान्वित करते हैं) तो हम इसकी InvokeRequired संपत्ति की जांच करते हैं, और यह सच है कि हम इसे प्रतिनिधि और पैरामीटर को पारित करने के लिए आमंत्रित करते हैं। इस तरह से कॉल करने से कॉल को यूआई थ्रेड में सिंक्रनाइज़ कर दिया जाएगा।

अन्यथा हम सीधे ईवेंट हैंडलर प्रतिनिधि को सीधे कॉल करते हैं।

+2

मैं क्योंकि मैं विस्तार '{के साथ एक' System.InvalidCastException' हो रही थी '' EventHandler' PropertyChangedEventHandler' के नाम बदलने के लिए किया था "प्रकार 'System.ComponentModel.PropertyChangedEventHandler' की वस्तु कास्ट करने में असमर्थ टाइप करने के लिए 'System.EventHandler'।" } ' मेरे पास एक बाइंडिंगलिस्ट है जो यूआई थ्रेड में बनाई गई है जो आंतरिक रूप से ईवेंट की सदस्यता लेती है, लेकिन सिंक वैरिएबल हमेशा शून्य देता है क्योंकि एच। लक्ष्य शून्य है। –

+0

मुझे @RickShealer के समान समस्या का सामना करना पड़ रहा है। तारीख को ध्यान में रखते हुए मुझे आश्चर्य है कि क्या यह नेट के नए संस्करणों के साथ एक मुद्दा है? यह क्रॉस-थ्रेड INotifyPropertyChanged समस्या के लिए एक बहुत ही सुरुचिपूर्ण समाधान की तरह लगता है, इसलिए मुझे आशा है कि हम इसे काम पर ला सकते हैं। – Jacob

+0

@ जैकोब मैं यह देखने के लिए नए ढांचे को लक्षित करूंगा कि क्या यह विफल हो जाता है। क्या आप मुझे अपने प्रोजेक्ट के ढांचे को बताएं लक्ष्य संस्करण या कोई अन्य जानकारी जो आप सोच सकते हैं वह उचित है? – Loudenvier

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