2012-08-24 51 views
0

後我試圖使用Task.Factory.StartNew()來運行後臺操作。部分後臺操作更新ObservableCollection中保存的對象。當使用從ObservableCollection派生的自定義類來觸發OnCollectionChanged()時,集合中的某個對象的屬性發生更改(請參閱https://stackoverflow.com/a/5256827/62072)。如果是綁定到的ObservableCollection一個CollectionView然後我得到一個異常:CollectionView NotSupportedException檢查Dispatcher.CurrentDispatcher.Thread.ManagedThreadId

System.NotSupportedException:這種類型的CollectionView不會從調度線程不同的線程支持更改其SourceCollection。

我試圖避免這種例外,所以我如果在UI線程上運行已經添加一些代碼來只火OnCollectionChanged()。但不知何故,我仍然得到例外..

這裏是我的ItemPropertyChanged()方法:

void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    var a = new NotifyCollectionChangedEventArgs(
     NotifyCollectionChangedAction.Reset); 

    if (Thread.CurrentThread.ManagedThreadId 
     == Dispatcher.CurrentDispatcher.Thread.ManagedThreadId) 
    { 
     OnCollectionChanged(a); 
    } 
} 

這裏是完整的異常:

System.AggregateException was unhandled 
    Message=A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. 
    Source=mscorlib 
    StackTrace: 
     at System.Threading.Tasks.TaskExceptionHolder.Finalize() 
     Message=This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread. 
     InnerException: System.NotSupportedException 
      Source=PresentationFramework 
      StackTrace: 
       at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args) 
       at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e) 
       at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
       at SourceLog.Model.TrulyObservableCollection`1.ItemPropertyChanged(Object sender, PropertyChangedEventArgs e) in C:\github.com\tomhunter-gh\SourceLog\SourceLog.Model\TrulyObservableCollection.cs:line 41 
       at System.ComponentModel.PropertyChangedEventHandler.Invoke(Object sender, PropertyChangedEventArgs e) 
       at SourceLog.Model.LogEntry.OnPropertyChanged(String property) in C:\github.com\tomhunter-gh\SourceLog\SourceLog.Model\LogEntry.cs:line 44 
       at SourceLog.Model.LogEntry.set_Read(Boolean value) in C:\github.com\tomhunter-gh\SourceLog\SourceLog.Model\LogEntry.cs:line 28 
       at SourceLog.Model.LogEntry.<MarkAsReadAndSave>b__0() in C:\github.com\tomhunter-gh\SourceLog\SourceLog.Model\LogEntry.cs:line 53 
       at System.Threading.Tasks.Task.InnerInvoke() 
       at System.Threading.Tasks.Task.Execute() 
      InnerException: 

異常怎麼會抱怨說我不上調度程序線程,當我明確檢查我是?

+0

這是一個可選的ObservableCollection,它允許從一個線程進行通知和更改,並且它會將所有同步處理回UI線程。 http://www.deanchalk.me.uk/post/Thread-Safe-Dispatcher-Safe-Observable-Collection-for-WPF.aspx ...確保你在UI線程中創建它...因爲它捕獲當前的調度器(在構造函數中)...然後您可以將引用傳遞給您的後臺線程使用。 –

回答

5

Dispatcher.CurrentDispatcher無關與UI線程,與物業顧名思義它給你當前線程的調度。所以你創建了一個總是返回true的支票。使用Application.Current.Dispatcher,而不是忽略可以在調度器上調用它的更改。

+0

那麼有多少'Dispatchers'在那裏?他沒有正確地檢查它 - 他應該[Dispatcher.CheckAccess()] [1]但是'System.Windows.Deployment.Dispatcher.BeginInvoke((=){});'會完成這項工作。不是嗎? [1]:http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess.aspx – Jasper

+0

@Jasper:調度程序的數量小於或等於所述數目線程,線程可能擁有或不擁有調度器。如果調用'CurrentDispatcher',則爲該線程創建一個新的調度程序,如果該線程尚未擁有該調度程序。正如我所看到的那樣'CheckAccess'是非常不必要的,如果你總是發送到UI調度程序,唯一的區別是你可以直接執行代碼而不是在處理隊列中推送它,應該不是什麼大不了的事情。 'BeginInvoke'可以工作,但它是異步的,所以你必須小心比賽條件。 –

+0

B.T.W:你有沒有注意到根異常來自'任務'?我正要建議SynchronizationContext [http://msdn.microsoft.com/en-us/library/wx31754f] class,但猜測我錯了。我想他/她在沒有同步上下文的情況下在'Task'內部提升'OnCollectionChanged'並且甚至沒有'Wait()'它... – Jasper

2

當您訪問可觀察集合時,您需要將上下文切換回UI線程。嘗試

Action del =() => {YourCodeHere()}; 
Dispatcher.Invoke(del);