Советы, где и когда использовать ObservableCollection в MvvmCross

В решении MvvmCross у меня есть одноэлементный класс службы, который получает элементы из веб-службы и обновляет общедоступную коллекцию ObservableCollection. Он делает это каждые пять секунд, и элементы могут быть добавлены или удалены, или их свойства могут быть изменены.

У меня также есть ViewModel с общедоступным свойством, для которого установлено значение ObservableCollection службы. Представление привязано к ObservableCollection, поэтому при добавлении, удалении или изменении элементов представление должно обновляться, чтобы показать это.

Однако, как и ожидалось, я получаю исключение потока, потому что ObservableCollection обновляется потоком, отличным от основного/UI, и поэтому привязка не может обновить UI.

В Службе у меня нет легкодоступного вызова InvokeOnMainThread, поэтому нет очевидного кросс-платформенного способа вернуться к основному потоку при обновлении ObservableCollection. Кроме того, это кажется неправильным - Служба не должна заниматься вопросами пользовательского интерфейса (в то время как ViewModel может).

Кроме того, я немного нервничаю по поводу раскрытия событий из службы, если это приводит к тому, что ViewModels не собирает мусор. Я заметил, что в серии N+1 @slodge http://mvvmcross.wordpress.com/ он использует Служба обмена сообщениями, по-видимому, чтобы избежать именно этого.

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

Любые предложения о том, как лучше всего реализовать это, будут оценены - спасибо.


person Jason Steele    schedule 27.08.2013    source источник


Ответы (1)


Первоначальное требование о том, что INotifyCollectionChanged должен вызываться в потоке пользовательского интерфейса, на самом деле исходит из синхронного способа, которым Windows управляет обновлением на основе уведомлений о добавлении/удалении/перемещении/замене/сбросе.

Это синхронное обновление, конечно, вполне разумно — было бы очень сложно обновить отображение пользовательского интерфейса, пока другой поток активно его изменяет.

В .Net 4.5 есть «новые» изменения, которые могут означать, что будущее будет лучше... но в целом они кажутся мне довольно сложными - см. https://stackoverflow.com/a/14602121/373321


Способы, которые я знаю, чтобы справиться с этим, по существу такие же, как и те, которые изложены в вашем посте:

A. сохранить ObservableCollection на уровне службы/модели и маршалировать все события там в поток пользовательского интерфейса - это возможно с использованием любого класса, который наследуется от MvxMainThreadDispatchingObject, или можно сделать, вызвав MvxMainThreadDispatcher.Instance.RequestMainThreadAction(action)

Хотя, к сожалению, это означает, что у вашей службы/модели есть некоторые знания о многопоточности, этот подход может хорошо работать для общего взаимодействия с приложением.

B. сделать дублирующую копию коллекции в ViewModel - обновив ее с помощью какого-либо механизма слабого ссылочного типа

  • например отправляя ему сообщения, которые говорят ему, что было добавлено, удалено, заменено или перемещено (или полностью сброшено) - обратите внимание, что для того, чтобы это работало, важно, чтобы сообщения поступали в правильном порядке!

  • или например разрешение отправки моментальных снимков из уровня службы/модели

Какой из них выбрать, зависит от:

  • частота, тип и размер коллекции меняются - например. получаете ли вы только случайные однострочные обновления, получаете ли вы частые большие всплески изменений или в основном видите сложные группы изменений (которые, по сути, являются Resets с точки зрения пользовательского интерфейса)
  • уровень анимации, необходимый в пользовательском интерфейсе - например. должны ли добавленные/удаленные элементы анимироваться? Если анимация не требуется, иногда бывает проще просто заменить весь список новым снимком, чем вносить пошаговые изменения.
  • размер самой коллекции - очевидно, что дублирование большой коллекции может вызвать проблемы с нехваткой памяти
  • постоянство, необходимое для коллекции - если требуется постоянство, то сам ObservableCollection может быть неподходящим, и вам может потребоваться использовать пользовательскую реализацию INotifyCollectionChanged (например, примеры MyCustomList)

Лично я обычно выбираю подход (A) в приложениях, но это зависит от ситуации, характеристик коллекции и ее изменений.

Обратите внимание, что, хотя это, безусловно, проблема mvvm, основная проблема не зависит от привязки данных — как вы обновляете отображение списка на экране, когда сам список изменяется в фоновом потоке?

person Stuart    schedule 27.08.2013
comment
Новый EnableCollectionSynchronization выглядит интересно, но вы правы - он действительно выглядит сложным, реализация блокировки доставляет столько же хлопот, сколько и упорядочение событий. Итак, в конце концов, похоже, что A IS выполнима, и я предполагаю, что если ViewModel не обрабатывает какие-либо из своих событий, то он не будет поддерживаться за счет ссылки из службы. Итак, еще раз спасибо за обстоятельный ответ и подсказки, как вернуться в Главную ветку из Сервиса. - person Jason Steele; 27.08.2013
comment
Я попробовал этот MvxMainThreadDispatcher.Instance.RequestMainThreadAction, но он не помогает :( Что еще можно сделать? - person DanilGholtsman; 10.12.2017
comment
stackoverflow.com/questions/47728293/ полная проблема - person DanilGholtsman; 11.12.2017