Съвет къде и кога да използвате ObservableCollection в MvvmCross

В решение на MvvmCross имам единичен сервизен клас, който получава елементи от уеб услуга и актуализира публична ObservableCollection. Той прави това на всеки пет секунди и елементите могат да се добавят или премахват или свойствата им да се променят.

Също така имам ViewModel, който има публично свойство, което е зададено на ObservableCollection на услугата. Изгледът е обвързан с ObservableCollection, така че когато елементите се добавят, премахват или променят, изгледът трябва да се актуализира, за да покаже това.

Въпреки това, както се очаква, получавам изключение за нишка, тъй като ObservableCollection се актуализира от нишка, различна от Main/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)

Въпреки че е жалко, че това означава, че вашата услуга/модел има известни познания за нишки, този подход може да работи добре за цялостното изживяване на приложението.

Б. направете дублирано копие на колекцията в 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