UI上的可观察集合更改不会从后台更新,但普通属性会收到通知

时间:2014-11-04 09:45:20

标签: c# wpf datagrid

我有两个班级

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.**
                }
            }
        });

为什么会出现这种行为?

4 个答案:

答案 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线程创建集合,然后使用您想要的任何线程中的集合。