Подтвердить что ты не робот

Обновление ObservableCollection в отдельном потоке

В приложении WPF ObservableCollection заполняется и обновляется с помощью запросов LINQ to SQL. Затем объекты UI обновляются с использованием значений из этого ObservableCollection.

Возможно ли и разумно, что операции по обновлению этих запросов ObservableCollection по LINQ to SQL выполнялись в отдельном потоке?

Если да, будет ли в этом случае это один и тот же экземпляр этого ObservableCollection? (Я имею в виду, если он не является тем же самым для принятия значений из LINQ datacontext, а другой для предоставления значений для обновления пользовательского интерфейса, то я не буду обновлять интерфейс)

4b9b3361

Ответ 1

Со встроенным классом ObservableCollection<T> вы не можете изменять контент из отдельного потока, если пользовательский интерфейс привязан к коллекции, он выдает NotSupportedException (но уведомление об изменении свойств элементов коллекции работает отлично). Я написал AsyncObservableCollection<T> класс для обработки этого случая. Он работает, вызывая обработчики событий в контексте синхронизации пользовательского интерфейса.

Ответ 2

.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

Ответ 3

В нашем приложении мы привязаны TreeView к ObservableCollection, который мы регулярно обновляем в фоновом потоке, запрашивая данные из нашего хранилища. Он отлично работает!

Упс. Я был неверно проинформирован =))

Правильно, мы на самом деле подклассифицируем ObservableCollection<T> и переопределяем метод OnCollectionChanged, чтобы избежать исключения crossthreading пользовательского интерфейса. Мы используем это решение:

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 из потока отличается от потока Диспетчер.

Теперь единственной проблемой является позиция выбранного элемента, в некоторых случаях, если выбранный элемент удаляется из коллекции, TreeView перемещает выделение на следующий элемент (что вызывает некоторые другие ненужные действия пользовательского интерфейса в нашем приложении). Но это небольшая проблема.

Ответ 4

Попытка понять ваш вопрос здесь:

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 вам придется использовать поток пользовательского интерфейса. В потоке пользовательского интерфейса принадлежит ObservableCollection, и вы получите исключение, если попытаетесь использовать его в другом потоке.

С Сценарием 2, палец вверх. Пока вы не пытаетесь добавить или удалить элементы из самой коллекции, вы можете обновить элемент столько, сколько хотите в фоновом потоке.