32

मैं एक ObservableCollection अद्यतन करने के लिए BackgroundWorker उपयोग कर रहा हूँ, लेकिन यह इस त्रुटि देता है:किसी अन्य थ्रेड से अवलोकन करने योग्य चयन को अपडेट करने का सबसे अच्छा तरीका क्या है?

"This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."

काम के कम से कम राशि के साथ इस को हल करने के लिए सबसे अच्छा और सबसे सुंदर तरीका है क्या। मैं निम्न स्तर के लॉक-आधारित बहु थ्रेडिंग कोड लिखना नहीं चाहता हूं।

मैंने कुछ समाधान ऑनलाइन देखे हैं लेकिन वे कई साल पुराने हैं, इसलिए सुनिश्चित करें कि इस समस्या के समाधान के लिए नवीनतम सर्वसम्मति क्या है।

+1

इसे जांचें: http://stackoverflow.com/questions/528999/why-arent-classes-like-bindinglist-or-observablecollection-thread-safe – geofftnz

+1

सटीक डुप्लिकेट: http://stackoverflow.com/questions/ 187,069/कर सकते हैं-न काम-ObservableCollection-इन-बहु धागे। मार्क इंग्राम का जवाब ऐसा लगता है जो आप खोज रहे हैं। –

+0

मैंने मार्क के जवाब को देखा लेकिन इसे समझ नहीं पाया। यह मॉनिटर का उपयोग करता है लेकिन DoEvents विधि क्या है? यह भी कैसे काम करता है, प्रयास पकड़ थोड़ी देर के बाहर है। क्या लॉक अभी भी उस बिंदु पर प्रभाव में है? –

उत्तर

12

MVVM

तो
public class MainWindowViewModel : ViewModel { 

    private ICommand loadcommand; 
    public ICommand LoadCommand { get { return loadcommand ?? (loadcommand = new RelayCommand(param => Load())); } } 

    private ObservableCollection<ViewModel> items; 
    public ObservableCollection<ViewModel> Items { 
     get { 
      if (items == null) { 
       items = new ObservableCollection<ViewModel>(); 
      } 
      return items; 
     } 
    } 

    public void Load() { 
     BackgroundWorker bgworker = new BackgroundWorker(); 
     bgworker.WorkerReportsProgress = true; 
     bgworker.DoWork += (s, e) => { 
      for(int i=0; i<10; i++) { 
       System.Threading.Thread.Sleep(1000); 
       bgworker.ReportProgress(i, new List<ViewModel>()); 
      } 
      e.Result = null; 
     }; 
     bgworker.ProgressChanged += (s, e) => { 
      List<ViewModel> partialresult = (List<ViewModel>)e.UserState; 
      partialresult.ForEach(i => { 
       Items.Add(i); 
      }); 
     }; 
     bgworker.RunWorkerCompleted += (s, e) => { 
      //do anything here 
     }; 
     bgworker.RunWorkerAsync(); 
    } 
} 
+0

धन्यवाद लेकिन रनवॉर्कर बीजीडब्ल्यू बाहर निकलने के बाद चलने वाला है? क्योंकि मैं चाहता हूं कि UI प्रत्येक पुनरावृत्ति पर अपडेट हो ताकि मैं तुरंत सभी परिवर्तन देख सकूं। –

+0

आप आंशिक अद्यतन करने के लिए RunWorker को पूर्ण कर सकते हैं, मुझे कोड – djeeg

+0

कोड अपडेट करने का मतलब है, मेरा मतलब है "प्रोग्रेस चेंज" – djeeg

4

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

+0

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

+0

मुझे पता नहीं है, आप कोड के ब्योरे के बारे में बात कर रहे हैं जो मैं नहीं देख सकता।ठंडा कठिन तथ्य यह है कि आप जो भी काम कर रहे हैं उसे अपडेट नहीं कर सकते हैं। इवेंट हैंडलरों में से एक का प्रयोग करें। मध्यस्थ परिणामों को स्टोर करने के लिए आवश्यक होने पर एक सहायक संग्रह का उपयोग करें। –

+0

यह काम करता है अगर मैं संग्रह संग्रह करता हूं, 1 आइटम जोड़ता हूं और इसे प्रतिस्थापित करता हूं, लेकिन यह हैक की तरह लग रहा था। क्या आपको लगता है कि यह बुरा अभ्यास है? –

36

आप निर्माता में संग्रह यह डिफ़ॉल्ट आवेदन धागा पर होगा प्रारंभ है।

मुख्य थ्रेड आप ऐसा कर सकते आह्वान करने के लिए:

Application.Current.Dispatcher.Invoke((Action)(() => 
    { 
     //Do something here. 
    })); 

आप एक कार्रवाई अन्यथा यह भ्रमित हो जाता है के रूप में बेनामी प्रतिनिधि कास्ट करने के लिए है ¯ \ O_o/¯

आप Async उपयोग कर रहे हैं सीटीपी तो आप इस

Application.Current.Dispatcher.InvokeAsync(()=> 
    { 
     //Do something here. 
    }); 
+0

धन्यवाद! मेरे लिये कार्य करता है :) –

1

मैं एक ही समस्या थी, जबकि एक घटना (DataReceived पर) से एक ObservableCollection पुन: लोड सीरियल पोर्ट वर्ग द्वारा उठाए गए कर सकते हैं। मैंने एमवीवीएम का उपयोग किया; मैंने BackgroundWorker के साथ संग्रह को अपडेट करने का प्रयास किया, लेकिन यह "इस प्रकार का संग्रह दृश्य उठाता है जो डिस्पैचर थ्रेड से अलग धागे से अपने स्रोत चयन में परिवर्तनों का समर्थन नहीं करता है।" मैं कई अन्य लोगों के समाधान ऑनलाइन पाया कोशिश की, लेकिन केवल एक ही है कि मेरी समस्या (! तुरंत) हल एक बहु सूत्रण नमूदार संग्रह का उपयोग किया गया (मैं इस पोस्ट में एक प्रयोग किया है: Where do I get a thread-safe CollectionView?)

3

इस प्रयास करें:

this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() => 
{ 

//Code 

}));