我有两个班级
public class ConccClass<T> : ObservableCollection<T>
{
}
和
public class TestTherad: INotifyPropertyChanged
{
private string name;
public string Name
{ get {
return name;
}
set
{
if (value != name)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
//// the notification implemented fully here
}
现在我已经创建了一个&#39; ConccClass&#39;在我的视图模型中,并在视图中使用xaml上的datagrid绑定它。
问题 当我在后台线程上添加一个项目而没有任何dispact时它不会反映在datagrid中。表示没有添加任何项目Todo这个我必须在Dispatcher中添加项目。 BeginInvoke的。这对我来说很有意义。
但要更新我不需要调度员的任何物品的名称。
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep(100);
{
this.Dispatcher.Invoke(() => this.Coll.Add(new TestTherad())); // **Works well**
//this.Coll.Add(new TestTherad()); // **does not work at all.**
this.Coll[0].Name = r.Next().ToString(); // ** without dispatcher works well.**
}
}
});
为什么会出现这种行为?
答案 0 :(得分:5)
以下是一个简短的解释:
您可能有一个绑定到可观察集合的UI元素。将元素添加到observable集合时,UI会更新以反映更改。但是,允许对UI进行更改的唯一线程是主线程。
因此,当您使用后台线程向可观察集合添加项目时,UI会尝试使用后台线程进行更新,后台线程不允许对UI进行更改,并且会抛出异常。
我很确定这行应该抛出异常://this.Coll.Add(new TestTherad());.尝试在任务块内部进行调试。
当您使用调度程序时,您正在使用主线程进行更新,因此它可以正常工作。
对属性的更新有效,因为您只是在举办活动。框架应该监听该事件并确保自动将其分发给主线程。
答案 1 :(得分:3)
避免这些异常的一种简单方法是使用Caliburn.Micro中的BindableCollection。 这是一个ObservableCollection,它会自动将CollectionChanged事件分派给主线程。
仅在ViewModel中使用BindableCollection,因为CollectionChanged事件将在主线程上,出于性能原因,您希望在后台线程上完成大部分编码。
答案 2 :(得分:1)
之前的大部分评论已经回答了这个问题,但是,这应该总结一下。在.NET 4.5之前,您只能在UI线程上调用ObservablCollection上的更新。这是通过调用Dispatcher.Invoke实现的。更新对象名称的调用不会影响集合事件等,只会影响对象。理想情况下,更新对象属性也应仅在UI线程上完成,但是,根据属性的绑定方式,有时可以从非ui线程中更新这些属性。如前所述,这将根据使用的.Net版本而有所不同。
答案 3 :(得分:0)
在Github,你会找到一个名为AsyncObservableCollection的小助手类:Github它是一个非常薄的OberserableCollection包装器。它接管了你提到的所有线程问题。您可以从您的ui线程创建集合,然后使用您想要的任何线程中的集合。