zoukankan      html  css  js  c++  java
  • 绑定到异步的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了。

  • 相关阅读:
    PCA降维以及维数的确定
    多元线性回归解决机器学习问题的一般方法
    修改commit记录的常用方法
    调用ocx ActiveX控件详解(做一个简单的ocx控件)
    nodeJS实现识别验证码(tesseract-ocr+GraphicsMagick)
    写一个自己的打包工具-打包原生项目
    React引入AntD按需加载报错
    用Canvas实现Photoshop的钢笔工具(贝塞尔曲线)
    offsetX、clientX、screenX、pageX、layerX
    WebSocket实现数据库更新前台实时显示
  • 原文地址:https://www.cnblogs.com/scy251147/p/2745760.html
Copyright © 2011-2022 走看看