我正在 WCF 数据服务异步查询回调方法中更新 WPF ViewModel 的 ObservableCollection:
ObservableCollection<Ent2> mymodcoll = new ObservableCollection<Ent2>();
...
query.BeginExecute(OnMyQueryComplete, query);
...
private void OnMyQueryComplete(IAsyncResult result)
{
...
var repcoll = query.EndExecute(result);
if (mymodcoll.Any())
{
foreach (Ent c in repcoll)
{
var myItem = mymodcoll.Where(p => p.EntID == c.EntID).FirstOrDefault();
if (myItem != null)
{
myItem.DateAndTime = c.DateAndTime; // here no problems
myItem.Description = c.Description;
...
}
else
{
mymodcoll.Add(new Ent2 //here I get a runtime error
{
EntID = c.EntID,
Description = c.Description,
DateAndTime = c.DateAndTime,
...
});
}
}
}
else
{
foreach (Ent c in repcoll)
{
mymodcoll.Add(new Ent2 //here, on initial filling, there's no error
{
EntID = c.EntID,
Description = c.Description,
DateAndTime = c.DateAndTime,
...
});
}
}
}
问题是,当查询结果集合包含目标集合中不存在的项目并且我需要添加该项目时,我会收到运行时错误:调用线程无法访问该对象因为另一个线程拥有它。(我通过评论指出了这行代码)
然而,如果目标集合为空(在初始填充时),所有项目都已添加,没有任何问题。 (这部分代码我也通过评论指出)。当一个项目只需要更新它的一些字段时,也没有问题,项目更新正常。
我该如何解决这个问题?
最佳答案
第一种情况:在这里您修改了集合中的对象,而不是集合本身 - 因此是 CollectionChanged
事件未被触发。
第二种情况:在这里,您从另一个线程 CollectionChanged
向集合中添加一个新元素。事件被触发。由于数据绑定(bind),该事件需要在UI线程中执行。
这个问题我已经遇到好几次了,解决方案不是很好(如果有人有更好的解决方案,请告诉我!)。您必须从 ObservableCollection<T>
派生并将其委托(delegate)给 BeginInvoke
或 Invoke
GUI 线程的调度程序上的方法。
例子:
public class SmartObservableCollection<T> : ObservableCollection<T>
{
[DebuggerStepThrough]
public SmartObservableCollection(Action<Action> dispatchingAction = null)
: base()
{
iSuspendCollectionChangeNotification = false;
if (dispatchingAction != null)
iDispatchingAction = dispatchingAction;
else
iDispatchingAction = a => a();
}
private bool iSuspendCollectionChangeNotification;
private Action<Action> iDispatchingAction;
[DebuggerStepThrough]
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!iSuspendCollectionChangeNotification)
{
using (IDisposable disposeable = this.BlockReentrancy())
{
iDispatchingAction(() =>
{
base.OnCollectionChanged(e);
});
}
}
}
[DebuggerStepThrough]
public void SuspendCollectionChangeNotification()
{
iSuspendCollectionChangeNotification = true;
}
[DebuggerStepThrough]
public void ResumeCollectionChangeNotification()
{
iSuspendCollectionChangeNotification = false;
}
[DebuggerStepThrough]
public void AddRange(IEnumerable<T> items)
{
this.SuspendCollectionChangeNotification();
try
{
foreach (var i in items)
{
base.InsertItem(base.Count, i);
}
}
finally
{
this.ResumeCollectionChangeNotification();
var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
this.OnCollectionChanged(arg);
}
}
}
关于c# - 将项目添加到 ObservableCollection 时的线程问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5781921/