2014-11-04 30 views
5

我有两个类观察集合变化将不更新UI从背景,但一个正常的属性获取通知

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 
} 

现在我已经在创建“ConccClass”的集合我查看模型并将其绑定到xaml中的datagrid中。

问题 当我在后台线程上添加一个项目而没有任何dispacther时,它不反映在datagrid中。意味着没有添加项目。 Todo,我必须在分派器中添加项目。 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

哪个版本的.Net? – OmegaMan 2014-11-09 23:15:02

+0

使用dotPeek,在'INotifyPropertyChanged'里面有'event PropertyChangedEventHandler PropertyChanged'。看看'PropertyChangedEventHandler'显示它是一个具有以下属性的代理'[HostProtection(SecurityAction.LinkDemand,SharedState = true)]'。我想知道[SharedState](http://msdn.microsoft.com/zh-cn/library/system.security.permissions.hostprotectionattribute.sharedstate(v = vs.110).aspx)是否允许委托成功运行主线程无需调用。 – 2014-11-10 03:21:27

+0

@OmegaMan - VS 2012. – 2014-11-12 06:51:30

回答

5

以下是一个简短说明:

您可能有一个绑定到observable集合的UI元素。将元素添加到可观察集合时,UI会更新以反映更改。但是,允许更改UI的唯一线程是主线程。

因此,当您使用后台线程向可观察集合中添加项目时,UI会尝试使用后台线程进行更新,后者不允许对UI进行更改并引发异常。

我很确定这行应该抛出一个异常://this.Coll.Add(new TestTherad());.尝试在任务块内进行调试。

当您使用调度程序时,您正在使用主线程进行更新,因此它起作用。

属性的更新工作,因为你只是引发一个事件。该框架应该监听该事件并确保将其自动发送到主线程。

+0

我测试过,即使我使用ContentControl绑定了Name属性,它在没有Dispatcher的情况下也能正常工作(不需要文本块)。我仍然相信这种行为背后有一个隐藏的原因。当然,我知道一个属性不是更新UI。但为什么? – 2014-11-12 06:59:20

+0

@DJ不能理解你的评论。 “属性的更新工作,因为你只是提出一个事件,框架应该监听这个事件,并确保自动将它分派到主线程。”不是答案吗? – Joe 2014-11-12 11:04:43

+0

对于迟到的回复感到抱歉。但是在这两种情况下,事件都是在一个收集更改事件中引发的,这个事件也在UI上进行侦听。在另一个它也是事件。两者都绑定在用户界面上。它有什么不同? – 2014-11-17 00:23:48

3

避免这些异常的简单方法是使用Caliburn.Micro中的BindableCollection。 这是一个ObservableCollection,它会自动将CollectionChanged事件分派给主线程。

仅在您的ViewModels中使用BindableCollection,因为CollectionChanged事件将位于主线程上,出于性能原因,您希望在后台线程上完成大部分编码。

+0

BindableCollection是好东西。这是一个很好的选择,除非你打算写这样的阻止observablecollection:http://xcalibursystems.com/2014/02/making-a-better-observablecollection-part-2-cross-threading/ – Xcalibur37 2014-11-11 00:44:34

+0

谢谢你的建议,但我我正在寻找我的答案。 – 2014-11-12 07:02:02

+1

'BindableCollection'被破坏,并且它永久保持了它在某个线程上修改列表的同时在另一个线程上绑定UI的谎言。除显式更改通知之外,还有其他条件可能导致集合被重新查询,可能会在更新期间导致未定义的行为。 – 2014-11-12 14:31:10

1

大多数以前的评论已经回答了这个问题,但是,这应该总结。在.NET 4.5之前,您只能在UI线程上的ObservablCollection上调用更新。这是通过调用Dispatcher.Invoke来实现的。更新对象名称的调用不会影响收集事件等,只会影响对象。更新对象属性理想情况下也应该只在UI线程上完成,但是,根据属性的绑定方式,有时可能会从非UI线程更新这些属性。如前所述,这取决于正在使用哪个版本的.Net。

0

在Github上,您会发现一个名为AsyncObservableCollection的小型帮助器类:Github它是OberserableCollection周围的一个非常薄的包装器。它接管了你提到的所有线程问题。您可以从您的UI线程创建集合,然后使用您想要的任何线程的集合。