我认为 WPF 中最好的事情之一是引入ObservableCollection。有了这个类,数据绑定变得轻而易举,开发人员不再需要编写自定义代码来获得与 INotifyPropertyChanged 相同的效果。
我今天在玩一些多线程代码来更新我的ObservableCollection并遇到了一个异常,并显示以下消息“这种类型的 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 SourceCollection。
从一个集合中得到一个跨线程异常是很奇怪的,但如果你仔细想想,它是有道理的。ObservableCollection通常用于 WPF 数据绑定,并在其内容被修改时在内部引发CollectionChanged事件。如果在不同线程上添加项目,这也意味着在该不同线程上引发CollectionChanged事件,并且 WPF 的数据绑定控件将尝试根据该更改进行刷新。我们都知道 UI 控件的跨线程是一个很大的禁忌,一个控件只能在创建它的同一个线程上访问,即 UI 线程。现在能看到联动了吗?为了克服这个问题,我们只需要在ObservableCollection返回到 WPF 术语中的正确线程或 Dispatcher。
这是我编写的用于编组的代码片段。此类将在其构造函数中获取对当前 Dispatcher(当前线程)的引用,因此您需要确保在同一线程上创建 ObservableCollection。这通常不是问题,除非您在不同的线程上创建 ObservableCollection。
public
class
ObservableCollectionMarshaler
{
public
Dispatcher Dispatcher {
get
;
set
; }
public
ObservableCollectionMarshaler()
{
Dispatcher = Dispatcher.CurrentDispatcher;
}
public
void
AddItem(ObservableCollection oc, T item)
{
if
(Dispatcher.CheckAccess())
{
oc.Add(item);
}
else
{
Dispatcher.Invoke(
new
Action(t => oc.Add(t)), DispatcherPriority.DataBind, item);
}
}
public
void
RemoveItem(ObservableCollection oc, T item)
{
if
(Dispatcher.CheckAccess())
{
oc.Remove(item);
}
else
{
Dispatcher.Invoke(
new
Action(t => oc.Remove(t)), DispatcherPriority.DataBind, item);
}
}
public
void
Clear(ObservableCollection oc)
{
if
(Dispatcher.CheckAccess())
{
oc.Clear();
}
else
{
Dispatcher.Invoke(
new
Action(() => oc.Clear()), DispatcherPriority.DataBind);
}
}
}
如果在构造函数中获取 Dispatcher.CurrentDispatcher 对您来说太不安全,您始终可以在构造函数参数中传入 Dispatcher。
如果您要进行大量异步处理,为 ObservableCollection 创建一个 Wrapper 类来处理编组逻辑可能是一个好主意。希望这对您有所帮助。