c# - thread - 別のスレッドでObservableCollectionを更新する




invoke label (3)

WPFアプリケーションでは、ObservableCollectionがLINQからSQLクエリによって埋められ、更新されます。 次に、このObservableCollectionの値を使用してUIオブジェクトが更新されます。

このObservableCollectionをLINQ to SQLクエリで更新する操作が別のスレッドで実行された可能性と妥当性はありますか?

もしそうであれば、このケースではObservableCollectionの1つの同じインスタンスになりますか? (つまり、LINQ datacontextとUIを更新するための値を与えるための値が同じでなければ、UIを更新することはできません)


あなたの質問をここで理解しようとしています:

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では、親指が上になります。 コレクション自体にアイテムを追加または削除しない限り、バックグラウンドスレッドで必要なだけアイテムを更新できます。


私たちのアプリではObservableCollectionにバインドされたTreeViewをバックグラウンドスレッドで定期的に更新し、ストレージからデータを要求しています。 それは完全に動作します!

うわー。 私は誤った=))

実際、 ObservableCollection<T>を実際にサブクラス化していて、 OnCollectionChangedメソッドをオーバーライドしているため、UIのクロススクロール例外が発生しません。 私たちはこのソリューションを使用しています

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は、Dispatcherスレッドとは異なるスレッドからのSourceCollectionへの変更をサポートしていません。

現在選択されているアイテムの位置が選択されている場合があります。現在選択されているアイテムがコレクションから削除された場合、ツリービューはそのアイテムを次のアイテムに移動します。 しかしそれは小さな問題です。


.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





linq-to-sql