博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
绑定到异步的ObservableCollection
阅读量:5993 次
发布时间:2019-06-20

本文共 5651 字,大约阅读时间需要 18 分钟。

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

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

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

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

ObservableCollection
users = new ObservableCollection
(); public ObservableCollection
Users { get { return users; } set { users = value; } }
///             /// 开启监听线程             ///               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();        }        ///                 /// 方法:处理接到的数据               ///                 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
: ObservableCollection
{ //获取当前线程的SynchronizationContext对象 private SynchronizationContext _synchronizationContext = SynchronizationContext.Current; public AsyncObservableCollection() { } public AsyncObservableCollection(IEnumerable
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
users = new AsyncObservableCollection
(); public AsyncObservableCollection
Users { get { return users; } set { users = value; } }

参考文章:

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

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

转载地址:http://cgvlx.baihongyu.com/

你可能感兴趣的文章
第十六课 lvm及磁盘小案例
查看>>
sed命令之练习集
查看>>
链表的代码实现
查看>>
mybatis学习笔记,问题总结
查看>>
Static、const、extern区别
查看>>
ios打地鼠游戏源码
查看>>
Linux基础教程 linux无密码ssh登录设置
查看>>
Linux日志文件总管——logrotate
查看>>
INNODB 关键特性
查看>>
计算机系统(一)
查看>>
Rancher Labs联手NeuVector,提供容器管理与安全解决方案
查看>>
如何在GO语言中使用Kubernetes API?
查看>>
OSPF总结
查看>>
YUM仓库构建(基于FTP的方式)
查看>>
开始从头学起C语言,第一个编程
查看>>
如何查看及修改oracle编码格式方法
查看>>
js 高级算法 - 动态规划
查看>>
硬盘测试——dd
查看>>
Mysql root用户无法使用grant
查看>>
DBA成长之路---mysql数据库服务基础(二)
查看>>