在进行WPF开发过程中,需要从一个新的线程中操作ObservableCollection,结果程序抛出一个NotSupportedException的
错误:
This type of CollectionView does not support changes to its SourceCollection
from
a thread different
from
the Dispatcher thread
看其字面意思是跨线程操作不被支持。
下面的代码展示了这种错误出现的根源:
ObservableCollection
public ObservableCollection
{
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
{
//获取当前线程的SynchronizationContext对象
private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
public AsyncObservableCollection() { }
public AsyncObservableCollection(IEnumerable
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
手机扫一扫
移动阅读更方便
你可能感兴趣的文章