Обновление ObservableCollection в отдельном потоке - PullRequest
13 голосов
/ 20 января 2010

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

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

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

Ответы [ 4 ]

26 голосов
/ 20 января 2010

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

22 голосов
/ 24 июля 2015

.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

6 голосов
/ 20 января 2010

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

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

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

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 перемещает выбор к следующему элементу (что вызывает некоторые другие ненужные действия пользовательского интерфейса в нашем приложении). Но это небольшая проблема.

2 голосов
/ 20 января 2010

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

<b>Scenario 1</b>
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.

<b>Scenario 2</b>
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, палец вверх.Если вы не пытаетесь добавлять или удалять элементы из самой коллекции, вы можете обновлять элемент столько раз, сколько хотите в фоновом потоке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...