2010-01-20 92 views
12

在WPF應用程序中,ObservableCollection由LINQ to SQL查詢填充和更新。然後使用此ObservableCollection中的值更新UI對象。在單獨的線程中更新ObservableCollection

LINQ to SQL查詢更新此ObservableCollection的操作是否可以合理地在單獨的線程中執行?

如果是,那麼在這種情況下,它將成爲這個ObservableCollection的同一個實例嗎? (我的意思是,如果它不同於從LINQ datacontext獲取值以及用於更新UI的值,那麼我將無法更新UI)

回答

24

使用內置的ObservableCollection<T>類,如果UI綁定到集合,則不能更改單獨線程中的內容,但會拋出NotSupportedException(但更改集合項屬性的通知工作正常)。我寫了一個AsyncObservableCollection<T> class來處理這種情況。它通過調用事件處理程序的UI同步環境

+0

一個可行的替代方案http://stackoverflow.com/questions/12881489/asynchronously-adding-to-observablecollection-or-an-alternative – Narkha 2013-09-25 15:53:33

+0

或者你可以嘗試這是完全線程安全的,從任何線程工作,並且可以由多個UI線程的數據綁定:http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection – Anthony 2014-04-15 00:59:06

+0

好收集的工作! – 2014-06-26 17:31:33

6

在我們的應用程序中,我們有一個TreeView綁定到一個ObservableCollection,我們經常在後臺線程中更新,從我們的存儲中請求數據。它完美的工作!

哎呦。我被誤通知=))

對,我們實際上是繼承ObservableCollection<T>並覆蓋OnCollectionChanged方法以避免UI crossthreading異常。我們使用this solution

public class MTObservableCollection<T> : ObservableCollection<T> 
{ 
    public override event NotifyCollectionChangedEventHandler CollectionChanged; 
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     var eh = CollectionChanged; 
     if (eh != null) 
     { 
     Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList() 
       let dpo = nh.Target as DispatcherObject 
       where dpo != null 
       select dpo.Dispatcher).FirstOrDefault(); 

     if (dispatcher != null && dispatcher.CheckAccess() == false) 
     { 
      dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e))); 
     } 
     else 
     { 
      foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()) 
       nh.Invoke(this, e); 
     } 
    } 
    } 
} 

如果沒有超越你會得到這樣的

System.NotSupportedException異常:這種類型 的的CollectionView不從它的 SourceCollection不 支持更改線程 與Dispatcher線程不同。

現在我們唯一的問題是所選擇的項目位置,在某些情況下,如果當前選擇的項目是從集合中刪除TreeView中移動選擇到下一個項目(這會導致其他一些不必要的UI操作我們應用程序)。但這是一個小問題。

+2

這怎麼可能?當然,您正在封送回UI線程以應用數據存儲中的更改?否則,你會得到一個異常... – 2010-01-20 20:42:04

+3

您必須創建一個ObservableCollection的子類,將CollectionChanged事件編組回到UI線程。我發現這比管理更新的ViewModel更容易管理。 – 2010-01-20 21:09:46

+0

謝謝,肯特,奈傑爾 - 你是對的!我糾正了我的答案,這是我的錯誤。 – 2010-01-21 09:21:38

2

試圖在這裏明白你的問題:

 
Scenario 1 
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A. 
2. Periodically, more data is retrieved from database and added to A. Old data is removed from A. 

Scenario 2 
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A. 
2. Periodically, data in A is updated with new data from database (no add/remove). 

與場景1,你將不得不使用UI線程。 UI線程擁有ObservableCollection,如果你嘗試在另一個線程中使用它,你會得到一個異常。

隨着情景2,豎起大拇指。只要不嘗試添加或刪除集合本身的項目,就可以在後臺線程中儘可能多地更新項目。

15

.Net 4.5在BindingOperations類中提供了一個解決方案。

您現在可以使用BindingOperations。EnableCollectionSynchronization方法如下:

private readonly object _personCollectionLock; 
private ObservableCollection<Person> _personCollection; 

public ObservableCollection<Person> PersonCollection 
{ 
    get { return _personCollection; } 
    set 
    { 
    _personCollection = value; 
    BindingOperations.EnableCollectionSynchronization(_personCollection, _personCollectionLock); 
    } 

我纔剛剛嘗試這在我的開發環境,但一切似乎正確的是現在的工作,當我更新從後臺線程的集合。

有這個解決方案在更深入的討論:http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification

該方法的MSDN項是:https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

相關問題