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

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

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

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

  1. ObservableCollection<UserListViewModel> users = new ObservableCollection<UserListViewModel>();
  2. public ObservableCollection<UserListViewModel> Users
  3. {
  4. get { return users; }
  5. set { users = value; }
  6. }
  1. /// <summary>
  2. /// 开启监听线程
  3. /// </summary>
  4. private void openListeningThread()
  5. {
  6. isRun = true;
  7. Thread t = new Thread(new ThreadStart(() =>
  8. {
  9. IPEndPoint ipEnd = new IPEndPoint(broadIPAddress, lanPort);
  10. try
  11. {
  12. while (isRun)
  13. {
  14. try
  15. {
  16. byte[] recInfo = listenClient.Receive(ref ipEnd); //接受内容,存储到byte数组中
  17. DealWithAcceptedInfo(recInfo); //处理接收到的数据
  18. }
  19. catch (Exception ex) { MessageBox.Show(ex.Message); }
  20. } listenClient.Close(); isRun = false;
  21. }
  22. catch (SocketException se) { throw new SocketException(); } //捕捉试图访问套接字时发生错误。
  23. catch (ObjectDisposedException oe) { throw new ObjectDisposedException(oe.Message); } //捕捉Socket 已关闭
  24. catch (InvalidOperationException pe) { throw new InvalidOperationException(pe.Message); } //捕捉试图不使用 Blocking 属性更改阻止模式。
  25. catch (Exception ex) { throw new Exception(ex.Message); }
  26. }));
  27.  
  28. t.Start();
  29. }
  30.  
  31. /// <summary>
  32. /// 方法:处理接到的数据
  33. /// </summary>
  34. private void DealWithAcceptedInfo(byte[] recData)
  35. {
  36. BinaryFormatter formatter = new BinaryFormatter();
  37. MessageFlag recvMessageFlag;
  38.  
  39. MemoryStream ms = new MemoryStream(recData);
  40. try { recvMessageFlag = (MessageFlag)formatter.Deserialize(ms); }
  41. catch (SerializationException e) { throw; }
  42.  
  43. UserListViewModel uListViewModel = new UserListViewModel(new UserDetailModel { MyIP = recvMessageFlag.UserIP, MyName = recvMessageFlag.UserName });
  44.  
  45. switch (recvMessageFlag.Flag)
  46. {
  47. case "0x00":
  48. //这里很关键,当检测到一个新的用户上线,那么我们需要给这个新用户发送自己的机器消息,以便新用户能够自动添加进列表中。
  49. SendInfoOnline(recvMessageFlag.UserIP);
  50.  
  51. if (!list.Contains(uListViewModel.MyInfo))
  52. {
  53. list.Add(uListViewModel.MyInfo);
  54. Users.Add(uListViewModel);
  55. }
  56.  
  57. break;
  58. case "0x01":
  59. //AddTextBox(, int titleOrContentFlag, int selfOrOthersFlag);
  60. //AddTextBox(string info, int titleOrContentFlag, int selfOrOthersFlag);
  61. break;
  62. case "0x02":
  63.  
  64. break;
  65. case "0x03":
  66. if (list.Contains(uListViewModel.MyInfo))
  67. {
  68. // list.Remove(uListViewModel.MyInfo);
  69. Users.Remove(uListViewModel);
  70. }
  71. break;
  72. default: break;
  73. }
  74. }

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

解决方法如下:

  1. public class AsyncObservableCollection<T> : ObservableCollection<T>
  2. {
  3. //获取当前线程的SynchronizationContext对象
  4. private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
  5. public AsyncObservableCollection() { }
  6. public AsyncObservableCollection(IEnumerable<T> list) : base(list) { }
  7. protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  8. {
  9.  
  10. if (SynchronizationContext.Current == _synchronizationContext)
  11. {
  12. //如果操作发生在同一个线程中,不需要进行跨线程执行
  13. RaiseCollectionChanged(e);
  14. }
  15. else
  16. {
  17. //如果不是发生在同一个线程中
  18. //准确说来,这里是在一个非UI线程中,需要进行UI的更新所进行的操作
  19. _synchronizationContext.Post(RaiseCollectionChanged, e);
  20. }
  21. }
  22. private void RaiseCollectionChanged(object param)
  23. {
  24. // 执行
  25. base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
  26. }
  27. protected override void OnPropertyChanged(PropertyChangedEventArgs e)
  28. {
  29. if (SynchronizationContext.Current == _synchronizationContext)
  30. {
  31. // Execute the PropertyChanged event on the current thread
  32. RaisePropertyChanged(e);
  33. }
  34. else
  35. {
  36. // Post the PropertyChanged event on the creator thread
  37. _synchronizationContext.Post(RaisePropertyChanged, e);
  38. }
  39. }
  40. private void RaisePropertyChanged(object param)
  41. {
  42. // We are in the creator thread, call the base implementation directly
  43. base.OnPropertyChanged((PropertyChangedEventArgs)param);
  44. }
  45. }

将上面的ObservableCollection替换掉即可。

  1. AsyncObservableCollection<UserListViewModel> users = new AsyncObservableCollection<UserListViewModel>();
  2. public AsyncObservableCollection<UserListViewModel> Users
  3. {
  4. get { return users; }
  5. set { users = value; }
  6. }

参考文章:[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. thinkphp 链接数据库

    ThinkPHP内置了抽象数据库访问层,把不同的数据库操作封装起来,我们只需要使用公共的Db类进行操作,而无需针对不同的数据库写不同的代码和底层实现,Db类会自动调用相应的数据库驱动来处理.目前的数据 ...

  2. 0908CSP-S模拟测试赛后总结

    我早就料到昨天会考两场2333 话说老师终于给模拟赛改名了啊. 距离NOIP祭日还有60天hhh. 以上是废话. %%%DeepinC无敌神 -rank1 zkt神.kx神.动动神 -rank2 有钱 ...

  3. C++——友元函数和友元类

    友元函数:让函数可以访问类的私有属性 #include <iostream> using namespace std; class A { public: friend class B;/ ...

  4. 尚学python课程---13、python基础语法

    尚学python课程---13.python基础语法 一.总结 一句话总结: legend2系统使我能够快速掌握一门语法,特别有用 pass 语句:空语句:是为了保持程序结构的完整性  :作用:比如: ...

  5. day23_3_configparse

    #!/usr/bin/env python# -*- coding:utf-8 -*-# ------------------------------------------------------- ...

  6. MDK 虚拟串口 *** error 30: undefined name of virtual register

    概念说明 查看已有的虚拟寄存器 输入指令: dir vtreg 可以看到没有要配置的虚拟寄存器SxIN和SxOUT,通过查询手册可以看到所有的虚拟寄存器类型: 说明不支持.

  7. (转)线程池 ExecutorService 详细介绍以及注意点区别

    线程池 ExecutorService 相信java开发都用到,这里做个简单笔记 一 Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池 ...

  8. spring中依赖注入

    理解依赖注入:参考https://blog.csdn.net/taijianyu/article/details/2338311 一.依赖注入让bean与bean之间以配置文件组织在一起,而不是以硬编 ...

  9. 筛法求欧拉函数(poj2478

    求1-n的欧拉函数的值 #include <iostream> #include <cstdio> #include <queue> #include <al ...

  10. 阿里云OSS简单上传本地文件

    上传本地文件 # -*- coding: utf-8 -*- import oss2 # 阿里云主账号AccessKey拥有所有API的访问权限,风险很高.强烈建议您创建并使用RAM账号进行API访问 ...