C# 此类型的CollectionView不支持从不同于Dispatcher线程的线程更改其SourceCollection

C# 此类型的CollectionView不支持从不同于Dispatcher线程的线程更改其SourceCollection,c#,wpf,xaml,silverlight,mvvm,C#,Wpf,Xaml,Silverlight,Mvvm,我有一个DataGrid,它通过异步方法从ViewModel填充数据。我的DataGrid是: <DataGrid ItemsSource="{Binding MatchObsCollection}" x:Name="dataGridParent" Style="{StaticResource EfesDataGridStyle}" HorizontalGridLinesBrush="#DADAD

我有一个DataGrid,它通过异步方法从ViewModel填充数据。我的DataGrid是:

<DataGrid ItemsSource="{Binding MatchObsCollection}"  x:Name="dataGridParent" 
                      Style="{StaticResource EfesDataGridStyle}" 
                      HorizontalGridLinesBrush="#DADADA" VerticalGridLinesBrush="#DADADA" Cursor="Hand" AutoGenerateColumns="False" 
                      RowDetailsVisibilityMode="Visible"  >

我正在使用在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<EfesBet.DataContract.GetMatchDetailsDC>();                

        }       

        List<EfesBet.DataContract.GetMatchDetailsDC> matchList;
        ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC> _matchObsCollection;

        public ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC> MatchObsCollection
        {
            get { return _matchObsCollection; }
            set
            {
                _matchObsCollection = value;
                OnPropertyChanged("MatchObsCollection");
            }
        }        
        //
        public void Load()
        {            
            matchList = new List<GetMatchDetailsDC>();
            matchList = proxy.GetMatch().ToList();

            foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList)
            {
                _matchObsCollection.Add(match);
            }

        }
public类MainWindowViewModel:WorkspaceViewModel,INotifyCollectionChanged
{        
MatchBLL MatchBLL=新的MatchBLL();
EFESBetsServiceReference.EfesBetClient代理=新EfesBetClient();
公共ICommand DoSomethingCommand{get;set;}
公共主窗口视图模型()
{
DoSomethingCommand=新的AsyncDelegateCommand(
()=>Load(),null,null,
(ex)=>Debug.WriteLine(ex.Message));
_MatchObjsCollection=新的ObservableCollection();
}       
列表匹配列表;
ObservableCollection\u matchObsCollection;
公共可观测集合MatchObsCollection
{
获取{return\u matchobscolection;}
设置
{
_MatchObjsCollection=值;
OnPropertyChanged(“MatchObjsCollection”);
}
}        
//
公共空荷载()
{            
匹配列表=新列表();
matchList=proxy.GetMatch().ToList();
foreach(匹配列表中的EfesBet.DataContract.GetMatchDetailsDC匹配)
{
_添加(匹配);
}
}
正如您在我的ViewModel中的Load()方法中所看到的,首先我从我的服务中获取matchList(这是一个DataContract类的列表)。然后通过foreach循环,我将我的matchList项插入到我的_MatchObjectCollection(这是DataContract类的一个ObservableCollection))。现在我得到了上面的错误(如标题所示)“此类型的CollectionView不支持从不同于Dispatcher线程的线程更改其SourceCollection”


有人能给我建议任何解决方案吗?此外,如果可能的话,我想知道如何在视图中绑定我的DataGrid,如果有更好的方法,我还想知道如何异步刷新它。

如果我没弄错的话,在WPF 4.5中,您应该能够毫无问题地完成这项工作

现在要解决这个问题,您应该使用同步上下文。在启动线程之前,您必须将同步上下文存储在ui线程中

var uiContext = SynchronizationContext.Current;
然后在线程中使用它:

uiContext.Send(x => _matchObsCollection.Add(match), null);
看看这首短裙

因为您的ObservableCollection是在UI线程上创建的,所以您只能从UI线程而不能从其他线程修改它。这称为

如果您需要更新从不同线程在UI线程上创建的对象,只需
将委托放在UI Dispatcher
上,就可以将其委托给UI线程。这样就可以了-

    public void Load()
    {
        matchList = new List<GetMatchDetailsDC>();
        matchList = proxy.GetMatch().ToList();

        foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList)
        {
            App.Current.Dispatcher.Invoke((Action)delegate // <--- HERE
            {
                _matchObsCollection.Add(match);
            });
        }
    }
public void Load()
{
匹配列表=新列表();
matchList=proxy.GetMatch().ToList();
foreach(匹配列表中的EfesBet.DataContract.GetMatchDetailsDC匹配)
{

App.Current.Dispatcher.Invoke((Action)delegate/我曾经遇到过同样的问题,并使用AsyncObservableCollection()解决了这个问题。

在我的例子中(我使用异步任务填充
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());
您可以这样做:

App.Current.Dispatcher.Invoke((System.Action)delegate
             {
               _matchObsCollection.Add(match)
             });
对于.NET 4.5+:您可以遵循Daniel的答案。在他的示例中,您向发布者提供了他们需要在正确线程上调用或调用的责任:

var uiContext = SynchronizationContext.Current;
uiContext.Send(x => _matchObsCollection.Add(match), null);
或者,您可以将责任放在您的服务/viewmodel/任何内容上,只需启用CollectionSynchronization。这样,如果您拨打电话,您就不必担心您在哪个线程上以及您在哪个线程上拨打电话。责任不再由发布者承担。 (这可能会给您带来一点性能开销,但在中央服务中这样做,可以为您节省大量异常,并简化应用程序维护。)

private static object_lock=new object();
公共主窗口视图模型()
{
// ...
_MatchObjsCollection=新的ObservableCollection();
BindingOperations.EnableCollectionSynchronization(\u matchObjsCollection,\u lock);
} 
更多信息:


在Visual Studio 2015(Pro)中,转到调试-->Windows-->线程,轻松调试并查看您在哪个线程上运行。

如果您使用BackgroundWorker,则应在UI的同一线程中引发事件

例如,如果您有两个视图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
    }
//代码隐藏或视图模型中的代码
var worker=新的BackgroundWorker();

worker.DoWork+=WorkerDoWork;//我也遇到了这个错误:

“此类型的CollectionView不支持从不同于Dispatcher线程的线程更改其SourceCollection”

原来我已经创建了一个名为“Release Android”的新配置,它是“Release”配置的一个副本,并使用它在归档管理器中创建新版本。我改回了“Release”配置,一切都很好。没有更多错误


希望这对某人有所帮助。

我在这里找到了一个解决方案: 你只需要创建一个新类并使用它,而不是ObservableCollection。它对我很有用

public class AsyncObservableCollection<T> : ObservableCollection<T>
{
    private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;

    public AsyncObservableCollection()
    {
    }

    public AsyncObservableCollection(IEnumerable<T> list)
        : base(list)
    {
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the CollectionChanged event on the current thread
            RaiseCollectionChanged(e);
        }
        else
        {
            // Raises the CollectionChanged event on the creator thread
            _synchronizationContext.Send(RaiseCollectionChanged, e);
        }
    }

    private void RaiseCollectionChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the PropertyChanged event on the current thread
            RaisePropertyChanged(e);
        }
        else
        {
            // Raises the PropertyChanged event on the creator thread
            _synchronizationContext.Send(RaisePropertyChanged, e);
        }
    }

    private void RaisePropertyChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnPropertyChanged((PropertyChangedEventArgs)param);
    }
}
公共类AsyncObservableCollection:ObservableCollection
{
私有同步上下文_SynchronizationContext=SynchronizationContext.Current;
公共AsyncObservableCollection()
{
}
公共AsyncObservableCollection(IEnumerable列表)
:基本(列表)
{
}
CollectionChanged上的受保护覆盖无效(NotifyCollectionChangedEventArgs e)
{
如果(同步)
public class AsyncObservableCollection<T> : ObservableCollection<T>
{
    private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;

    public AsyncObservableCollection()
    {
    }

    public AsyncObservableCollection(IEnumerable<T> list)
        : base(list)
    {
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the CollectionChanged event on the current thread
            RaiseCollectionChanged(e);
        }
        else
        {
            // Raises the CollectionChanged event on the creator thread
            _synchronizationContext.Send(RaiseCollectionChanged, e);
        }
    }

    private void RaiseCollectionChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the PropertyChanged event on the current thread
            RaisePropertyChanged(e);
        }
        else
        {
            // Raises the PropertyChanged event on the creator thread
            _synchronizationContext.Send(RaisePropertyChanged, e);
        }
    }

    private void RaisePropertyChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnPropertyChanged((PropertyChangedEventArgs)param);
    }
}