绑定到异步的ObservableCollection
在进行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了。
转载于:https://www.cnblogs.com/scy251147/archive/2012/10/30/2745760.html
绑定到异步的ObservableCollection相关推荐
- Silverlight实用窍门系列:47.Silverlight中元素到元素的绑定,以及ObservableCollection和List的使用区别...
问题一:在某一些情况下,我们使用MVVM模式的时候,对于某一个字段(AgeField)需要在前台的很多个控件(A.B.C.D.E)进行绑定,但是如何能够让我们后台字段名改变的时候能够非常方便的改变所有 ...
- Service中的绑定服务总结
绑定服务是客户端服务器接口中的服务器,绑定服务可以让组件绑定到服务.发送请求.接收响应,甚至执行进程间通信IPC,绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行. 绑定服务是S ...
- ObservableCollection 类 详解
假设您正在创建 Windows 窗体应用程序,并且已将 DataGridView 控件绑定到标准 List(Of Customer) 数据结构.您希望能够使网格中的项目与基础数据源中的值保持同步.也就 ...
- 关于mvvm的ComboBox绑定SelectedValue值不正确问题
我的自定义下拉对象 public class ComboBoxModel{//基础属性public int SelectedIndex { get; set; }public string Selec ...
- 分享大麦UWP版本开发历程-03.GridView或ListView 滚动底部自动加载后续数据
今天跟大家分享的是大麦UWP客户端,在分类.订单或是搜索时都用到的一个小技巧,技术粗糙大神勿喷. 以大麦分类举例,默认打开的时候,会为用户展示20条数据,当用户滚动鼠标或者使用手势将列表滑动到倒数第二 ...
- Android ServiceConnection
绑定到一个Service 应用组件(客户端)可以调用bindService()绑定到一个service.Android系统之后调用service的onBind()方法,它返回一个用来与service交 ...
- [C# 项目实战]: 制作一个备忘录(经典)
01 - 概述 前几天群里有人问如何制作备忘录,感觉这样一个小实例挺适合新手们入门学习使用,所以就抽空做了出来.界面如下图: 这个备忘录主要包括了如下功能: ① 备忘录信息的增.删.改.查: ② 备忘 ...
- C#使用Xamarin开发可移植移动应用进阶篇(10.综合演练,来一份增删改查CRUD)
说点什么.. 呃 也有半个月没更新了. 本来这篇的Demo早就写完了,文章也构思好了.迟迟没发布..是因为实在太忙.. 项目要上线..各种 你们懂的.. 正赶上自己十一人生大事..结婚..所以..忙 ...
- Android 进程间通信——Service、Messenger
概述 介绍绑定服务端的三种方式:同一进程绑定服务.跨进程绑定服务(Messenger).跨进程绑定服务(aidl). 重点说一下通过Messenger.Service实现的进程间通信. 详细 代码下载 ...
最新文章
- C++这三点对你很重要
- apache rewrite 规则转换 nginx rewrite 的网站
- WebSocket服务器和客户端的一对多通知实现
- 为何小程序上线了,他们的内心却留下遗憾?
- 聊聊这两天刷屏的OpenAI新作,你注意到CLIP了吗
- 中画图title函数_Matlab对量子力学中的一维无限深势阱的模拟计算
- 面向接口编程详解---编程实例
- chrome 下载东西 失败禁止_chrome谷歌浏览器下载文件失败如何解决
- java 怎么使用 设计模式对业务进行解耦(一)
- python有道批量单词音标整理-Python 批量翻译 使用有道api;
- ps使用教程 核心蒙版
- java 分词获取词性_Ansj中文分词Java开发词性分类
- 笔记本当服务器显示器怎么连接,显示器怎么连接笔记本_显示器怎么接笔记本-win7之家...
- htc android sd卡,HTC M8支持扩展储存卡吗?HTC M8支持多大的储存卡?
- Linux挂载Windows共享文件夹
- 争做“拼购村” 中国“家电之都”有了新致富路
- Mac端Python+Appium环境搭建
- python项目练习四:新闻聚合
- linux两个光驱,llinux挂载多个光驱
- 以电子签名形式订立的合同具有证据效力吗?