Этот тип CollectionView не поддерживает изменения в SourceCollection из streamа, отличного от streamа Dispatcher

У меня есть DataGrid, который заполняет данные из ViewModel асинхронным методом. Мой DataGrid:

 

Я использую http://www.amazedsaint.com/2010/10/asynchronous-delegate-command-for-your.html для реализации асинхронного способа в моей модели viewmodel.

Вот мой код 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); } } 

Как вы можете видеть в моем методе Load () в моей модели ViewModel, сначала я получаю matchList (который является списком classа DataContract) из моего сервиса. Затем через цикл foreach я вставляю элементы matchList в свой _matchObsCollection (который является ObservableCollection из DataContract Class)). Теперь я получаю вышеуказанную ошибку (как я показал в заголовке) «Этот тип CollectionView не поддерживает изменения в SourceCollection из streamа, отличного от streamа Dispatcher» введите описание изображения здесь

Может ли кто-нибудь предложить мне какое-либо решение. Если возможно, мне хотелось бы узнать, как связать мой DataGrid в представлении, а также обновить его асинхронно, если есть лучший способ.

Поскольку ваш ObservableCollection создается в streamе пользовательского интерфейса, вы можете изменять его только из streamа пользовательского интерфейса, а не из других streamов. Это называется сходством streamов .

Если вам когда-либо понадобится обновлять объекты, созданные в streamе пользовательского интерфейса из другого streamа, просто put the delegate on UI Dispatcher и это будет работать для вас делегирования его в stream пользовательского интерфейса. Это будет работать –

  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); }); } } 

Если я не ошибаюсь, в WPF 4.5 вы сможете сделать это без каких-либо проблем.

Теперь, чтобы решить эту проблему, вы должны использовать контекст синхронизации. Перед запуском streamа вам необходимо сохранить контекст синхронизации в streamе ui.

 var uiContext = SynchronizationContext.Current; 

Затем вы используете его в своем streamе:

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

Взгляните на эту туту http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I

Вы можете сделать это:

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

Для .NET 4.5+: вы можете следовать ответам Даниэля. В своем примере вы даете ответчику издателю, что им нужно вызвать или вызвать в правильной теме:

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

Или вы можете установить ответственность за свой сервис / viewmodel / whatever и просто включить CollectionSynchronization. Таким образом, если вы звоните, вам не нужно беспокоиться о том, на каком streamе вы находитесь, и по которому вы совершаете вызов. Ответственность не для издателя. (Это может дать вам небольшую служебную нагрузку, но это делается в центральной службе, это может сэкономить вам много исключений и упростить обслуживание приложений.)

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

Дополнительная информация: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

В Visual Studio 2015 (Pro) перейдите в Debug -> Windows -> Threads, чтобы легко отлаживать и видеть, на каких streamах вы находитесь.

Однажды я столкнулся с одной проблемой и разрешил проблему с AsyncObservableCollection ( http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ ).

В моем случае (я заполняю ObservableCollection асинхронными задачами и не имею доступа к экземпляру App ) Я использую TaskScheduler.FromCurrentSynchronizationContext() для очистки коллекции при сбое:

  // 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()); 

Если вы используете BackgroundWorker, вы должны поднять это событие в том же streamе пользовательского интерфейса.

Например, если у вас есть два вида A и B, а следующий код внутри A вызывает событие 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 } 

Метод WorkerDoWork выполняется в streamе, который не совпадает с пользовательским интерфейсом.

  • Связывание видимости для DataGridColumn в WPF
  • Изображение кнопки мыши WPF
  • Обратный вызов, когда свойство зависимостей получает изменение xaml
  • Как передать значения (параметры) между страницами XAML?
  • WPF TreeView HierarchicalDataTemplate - привязка к объекту с несколькими дочерними коллекциями
  • Имя не существует в ошибке пространства имен в XAML
  • Связывание в WPF с элементом массива, заданным свойством
  • Переключение UWP в Style Setter не работает
  • WPF ListView: привязка события двойного щелчка (по элементу)
  • Применение инсульта к текстовому блоку в WPF
  • Значение * (звездочка) в WPF ColumnDefinition?
  • Давайте будем гением компьютера.