Questo tipo di CollectionView non supporta le modifiche a SourceCollection da un thread diverso dal thread Dispatcher

Ho un DataGrid che sta popolando i dati da ViewModel con il metodo asincrono. My DataGrid è:

 

Sto usando http://www.amazedsaint.com/2010/10/asynchronous-delegate-command-for-your.html per implementare il modo asincrono nel mio viewmodel.

Ecco il mio codice viewmodel:

 public class MainWindowViewModel:WorkspaceViewModel,INotifyCollectionChanged { MatchBLL matchBLL = new MatchBLL(); EfesBetServiceReference.EfesBetClient proxy = new EfesBetClient(); public ICommand DoSomethingCommand { get; set; } public MainWindowViewModel() { DoSomethingCommand = new AsyncDelegateCommand( () => Load(), null, null, (ex) => Debug.WriteLine(ex.Message)); _matchObsCollection = new ObservableCollection(); } List matchList; ObservableCollection _matchObsCollection; public ObservableCollection MatchObsCollection { get { return _matchObsCollection; } set { _matchObsCollection = value; OnPropertyChanged("MatchObsCollection"); } } // public void Load() { matchList = new List(); matchList = proxy.GetMatch().ToList(); foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList) { _matchObsCollection.Add(match); } } 

Come puoi vedere nel mio metodo Load () nel mio ViewModel, prima ricevo il matchList (che è un elenco di una class DataContract) dal mio servizio. Quindi tramite il ciclo foreach sto inserendo i miei elementi matchList nel mio _matchObsCollection (che è ObservableCollection di DataContract Class)). Ora qui sto ottenendo l’errore di cui sopra (come ho mostrato nel titolo) “Questo tipo di CollectionView non supporta le modifiche alla sua SourceCollection da un thread diverso dal thread Dispatcher” inserisci la descrizione dell'immagine qui

Qualcuno mi può suggerire qualsiasi soluzione. Inoltre, se ansible vorrei sapere come colbind il mio DataGrid in Visualizza e anche aggiornare in modo asincrono se c’è un modo migliore.

Poiché ObservableCollection viene creato sul thread dell’interfaccia utente, è ansible modificarlo solo dal thread dell’interfaccia utente e non da altri thread. Questo è definito come affinità di thread .

Se è necessario aggiornare gli oggetti creati sul thread dell’interfaccia utente da thread diversi, è sufficiente put the delegate on UI Dispatcher e ciò funzionerà per te delegandolo al thread dell’interfaccia utente. Questo funzionerà –

  public void Load() { matchList = new List(); matchList = proxy.GetMatch().ToList(); foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList) { App.Current.Dispatcher.Invoke((Action)delegate // <--- HERE { _matchObsCollection.Add(match); }); } } 

Se non sbaglio, in WPF 4.5 dovresti riuscire a farlo senza problemi.

Ora per risolvere questo, dovresti usare il contesto di sincronizzazione. Prima di avviare il thread, è necessario memorizzare il contesto di sincronizzazione nel thread ui.

 var uiContext = SynchronizationContext.Current; 

Quindi lo usi nella tua discussione:

 uiContext.Send(x => _matchObsCollection.Add(match), null); 

Dai un’occhiata a questo tutorial http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part- I

Puoi farlo:

 App.Current.Dispatcher.Invoke((System.Action)delegate { _matchObsCollection.Add(match) }); 

Per .NET 4.5+: puoi seguire la risposta di Daniel. Nel suo esempio si dà la responsabilità al publisher che devono chiamare o invocare il thread corretto:

 var uiContext = SynchronizationContext.Current; uiContext.Send(x => _matchObsCollection.Add(match), null); 

O potresti mettere la responsabilità al tuo servizio / viewmodel / a qualsiasi altra cosa e abilitare semplicemente CollectionSynchronization. In questo modo, se effettui una chiamata, non ti devi preoccupare di quale thread sei attivo e su quale telefonare. La responsabilità non è più per l’editore. (Ciò potrebbe comportare un piccolo sovraccarico di prestazioni, ma farlo in un servizio centrale, può farti risparmiare molte eccezioni e ti facilita la manutenzione delle applicazioni.)

 private static object _lock = new object(); public MainWindowViewModel() { // ... _matchObsCollection = new ObservableCollection(); BindingOperations.EnableCollectionSynchronization(_matchObsCollection , _lock); } 

Maggiori informazioni: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

In Visual Studio 2015 (Pro) vai a Debug -> Windows -> Thread per eseguire facilmente il debug e vedere su quali thread sei attivo.

Ho riscontrato lo stesso problema una volta e risolto il problema con AsyncObservableCollection ( http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ ).

Nel mio caso (popolino ObservableCollection con attività asincrone e non ho accesso all’istanza App ) utilizzo TaskScheduler.FromCurrentSynchronizationContext() per ripulire la raccolta in caso di errore:

  // some main task Task loadFileTask = Task.Factory.StartNew(...); Task cleanupTask = loadFileTask.ContinueWith( (antecedent) => { CleanupFileList(); }, /* do not cancel this task */ CancellationToken.None, /* run only if faulted main task */ TaskContinuationOptions.OnlyOnFaulted, /* use main SynchronizationContext */ TaskScheduler.FromCurrentSynchronizationContext()); 

Se utilizzi BackgroundWorker, devi generare l’evento nello stesso thread dell’interfaccia utente.

Ad esempio se hai due visualizzazioni A e B e il seguente codice all’interno di A solleva l’evento WakeUpEvent

 //Code inside codebehind or viewmodel of A var worker = new BackgroundWorker(); worker.DoWork += WorkerDoWork; //<-- Don't raise the event WakeUpEvent inside this method worker.RunWorkerCompleted += workerRunWorkerCompleted; // <-- Raise the event WakeUpEvent inside this method instead worker.RunWorkerAsync(); //Code inside codebehind or viewmodel of view B public ViewB () { WakeUpEvent += UpdateUICallBack; } private void UpdateUICallBack() { //Update here UI element } 

Il metodo WorkerDoWork viene eseguito in un thread che non è lo stesso dell'interfaccia utente.