在进行WPF开发过程中,需要从一个新的线程中操作ObservableCollection,结果程序抛出一个NotSupportedException的错误:

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

看其字面意思是跨线程操作不被支持。

下面的代码展示了这种错误出现的根源:

  ObservableCollection<UserListViewModel> users = new ObservableCollection<UserListViewModel>();
public ObservableCollection<UserListViewModel> Users
{
get { return users; }
set { users = value; }
}
        /// <summary>
/// 开启监听线程
/// </summary>
private void openListeningThread()
{
isRun = true;
Thread t = new Thread(new ThreadStart(() =>
{
IPEndPoint ipEnd = new IPEndPoint(broadIPAddress, lanPort);
try
{
while (isRun)
{
try
{
byte[] recInfo = listenClient.Receive(ref ipEnd); //接受内容,存储到byte数组中
DealWithAcceptedInfo(recInfo); //处理接收到的数据
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
} listenClient.Close(); isRun = false;
}
catch (SocketException se) { throw new SocketException(); } //捕捉试图访问套接字时发生错误。
catch (ObjectDisposedException oe) { throw new ObjectDisposedException(oe.Message); } //捕捉Socket 已关闭
catch (InvalidOperationException pe) { throw new InvalidOperationException(pe.Message); } //捕捉试图不使用 Blocking 属性更改阻止模式。
catch (Exception ex) { throw new Exception(ex.Message); }
})); t.Start();
} /// <summary>
/// 方法:处理接到的数据
/// </summary>
private void DealWithAcceptedInfo(byte[] recData)
{
BinaryFormatter formatter = new BinaryFormatter();
MessageFlag recvMessageFlag; MemoryStream ms = new MemoryStream(recData);
try { recvMessageFlag = (MessageFlag)formatter.Deserialize(ms); }
catch (SerializationException e) { throw; } UserListViewModel uListViewModel = new UserListViewModel(new UserDetailModel { MyIP = recvMessageFlag.UserIP, MyName = recvMessageFlag.UserName }); switch (recvMessageFlag.Flag)
{
case "0x00":
//这里很关键,当检测到一个新的用户上线,那么我们需要给这个新用户发送自己的机器消息,以便新用户能够自动添加进列表中。
SendInfoOnline(recvMessageFlag.UserIP); if (!list.Contains(uListViewModel.MyInfo))
{
list.Add(uListViewModel.MyInfo);
Users.Add(uListViewModel);
} break;
case "0x01":
//AddTextBox(, int titleOrContentFlag, int selfOrOthersFlag);
//AddTextBox(string info, int titleOrContentFlag, int selfOrOthersFlag);
break;
case "0x02": break;
case "0x03":
if (list.Contains(uListViewModel.MyInfo))
{
// list.Remove(uListViewModel.MyInfo);
Users.Remove(uListViewModel);
}
break;
default: break;
}
}

上面的方法如果在一个新的Thread中创建,就将会产生这种问题。

解决方法如下:

public class AsyncObservableCollection<T> : ObservableCollection<T>
{
//获取当前线程的SynchronizationContext对象
private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
public AsyncObservableCollection() { }
public AsyncObservableCollection(IEnumerable<T> list) : base(list) { }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{ if (SynchronizationContext.Current == _synchronizationContext)
{
//如果操作发生在同一个线程中,不需要进行跨线程执行
RaiseCollectionChanged(e);
}
else
{
//如果不是发生在同一个线程中
//准确说来,这里是在一个非UI线程中,需要进行UI的更新所进行的操作
_synchronizationContext.Post(RaiseCollectionChanged, e);
}
}
private void RaiseCollectionChanged(object param)
{
// 执行
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
{
// Post the PropertyChanged event on the creator thread
_synchronizationContext.Post(RaisePropertyChanged, e);
}
}
private void RaisePropertyChanged(object param)
{
// We are in the creator thread, call the base implementation directly
base.OnPropertyChanged((PropertyChangedEventArgs)param);
}
}

将上面的ObservableCollection替换掉即可。

        AsyncObservableCollection<UserListViewModel> users = new AsyncObservableCollection<UserListViewModel>();
public AsyncObservableCollection<UserListViewModel> Users
{
get { return users; }
set { users = value; }
}

参考文章:[WPF] Binding to an asynchronous collection

之所以利用SynchronizationContext,我觉得是因为后台处理线程和UI进行交互的时候,没有获取到SynchronizationContext的状态导致的。因为后台和前台的线程交互,需要通过SynchronizationContext的Send或者Post方法才能避免线程Exception。

有人说可以利用Control.Invoke方法来实现啊。。。实现个鬼啊,这里就没有Control.....你只能自己来同步SynchronizationContext了。

参考文章来源:http://www.cnblogs.com/scy251147/archive/2012/10/30/2745760.html

[转]绑定到异步的ObservableCollection的更多相关文章

  1. 绑定到异步的ObservableCollection

    原文:绑定到异步的ObservableCollection 在进行WPF开发过程中,需要从一个新的线程中操作ObservableCollection,结果程序抛出一个NotSupportedExcep ...

  2. C# Winform DataGrid 绑定List<> Or ObservableCollection<> 类型无法自动刷新问题

    当DataGrid通过绑定List<> Or ObservableCollection<> 类型数据,通过INofityPropertyChanged接口通知数据改变进行刷新无 ...

  3. Silverlight实用窍门系列:47.Silverlight中元素到元素的绑定,以及ObservableCollection和List的使用区别

    问题一:在某一些情况下,我们使用MVVM模式的时候,对于某一个字段(AgeField)需要在前台的很多个控件(A.B.C.D.E)进行绑定,但是如何能够让我们后台字段名改变的时候能够非常方便的改变所有 ...

  4. 常见.NET功能代码汇总 (2)

    常见.NET功能代码汇总 23,获取和设置分级缓存 获取缓存:首先从本地缓存获取,如果没有,再去读取分布式缓存写缓存:同时写本地缓存和分布式缓存 private static T GetGradeCa ...

  5. 【WPF】WPF中的List<T>和ObservableCollection<T>

    在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...

  6. Android -- Service绑定解绑和aidl

    Service是安卓四大组件之一,先前讲到了Service的生命周期,以及非绑定类型的生命周期的例子,这次来分享一下绑定形式的. 应用组件(客户端)可以调用bindService()绑定到一个serv ...

  7. ObservableCollection 类

    假设您正在创建 Windows 窗体应用程序,并且已将 DataGridView 控件绑定到标准 List(Of Customer) 数据结构.您希望能够使网格中的项目与基础数据源中的值保持同步.也就 ...

  8. UWP 双向绑定,在ListView中有个TextBox,怎么获取Text的值

    要求:评论宝贝的时候一个订单里面包含多个产品,获取对产品的评论内容哦 1. xaml界面 <ListView x:Name="lvDetail"> <ListVi ...

  9. WPF 目录树绑定 与 遍历

    定义树节点,(编译环境VS2017) public class GBTreeNode : INotifyPropertyChanged { private string _deviceId = str ...

随机推荐

  1. Java迷宫代码,深度优先遍历

    此次迷宫深度优先遍历寻找路径采用栈结构,每个节点都有固定的行走方向(右下左上),除非一个方向走不通,不然会一条道走到黑. 如果路径存在,打印出行走路径,否则打印出迷宫不存在有效路径. 方向常量定义: ...

  2. Last_SQL_Error: Error 'Can't drop database

    此文办法只用应急, 别的办法我还没想到,  文章是Copy来的 MySQL主从同步报错排错结果及修复过程之:Slave_SQL_Running: No 起因调查: 收到大量邮件报警想必事出有因,就问同 ...

  3. 【JZOJ6354】最短路(tiring)

    description analysis 显然边权有变化规律\(x,{1\over{x-1}},{x-1\over x},x,...\) 于是把一个点拆成三个点,分别表示步数到除\(3\)余\(0,1 ...

  4. C++ strcat_s

    { //如果用strcat() 连接时被连接的字符串是堆内存会 [损坏内存],使堆内存无法释放 建议用strcat_s() }

  5. 20175323《Java程序设计》第三周学习总结

    教材学习内容总结 这周开始用幕布记录学习过程和思路,下面是我这章的知识框架总结https://mubu.com/doc/aNMW9Clym0 教材学习中的问题和解决过程 问题1:教材90页的Trian ...

  6. Http学习(三)

    HTTP的问题: 通信使用明文,可能会遭到窃听:HTTP本身不具备加密功能,根据TCP/IP协议工作的线路上可能会遭到窃听,即使通信内容已经加密,也会被看到 通信加密:通过SSL(Secure Soc ...

  7. django-filter 实现过滤时查询是否包含在数组的方法,in数组的实现

    查了半天无解,还是在官网找到的,记录一下 使用 BaseInFilter 官网地址:https://django-filter.readthedocs.io/en/master/ref/filters ...

  8. MVC到底是设计模式还是一种框架还是一种架构? https://www.zhihu.com/question/31079945

    具体知乎讨论内容:https://www.zhihu.com/question/31079945 MVC到底是设计模式还是一种框架还是一种架构? 我认为它是3种设计模式的演变和组合:观察者模式(Obs ...

  9. 3列滚动抽奖 jquery.slotmachine

    效果图: 需引入js文件: <script src="js/jquery-3.2.0.js"></script> <script src=" ...

  10. ES6和常用特性归纳

    ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.Mozilla公司将在这个标准的基础上,推出JavaScript 2.0. ECMAS ...