【问题标题】:Advice on where and when to use ObservableCollection in MvvmCross关于在 MvvmCross 中何时何地使用 ObservableCollection 的建议
【发布时间】:2024-04-30 08:25:02
【问题描述】:

在 MvvmCross 解决方案中,我有一个单例服务类,它从 Web 服务获取项目并更新公共 ObservableCollection。它每五秒执行一次,并且可以添加或删除项目或更改它们的属性。

我还有一个 ViewModel,它有一个设置为服务的 ObservableCollection 的公共属性。 View 绑定到 ObservableCollection,因此当添加、删除或更改项目时,视图应该更新以显示这一点。

但是,正如预期的那样,我遇到了线程异常,因为 ObservableCollection 正在由 Main/UI 之外的线程更新,因此绑定无法更新 UI。

在服务中,我没有现成的InvokeOnMainThread 调用,因此在更新 ObservableCollection 时没有明显的跨平台方法可以返回到主线程。此外,这样做似乎是错误的——Service 不应该关注 UI 问题(而 ViewModel 可以)。

此外,如果这会导致 ViewModel 无法被垃圾收集,我对从服务中公开事件感到有点紧张。我注意到在@slodge 的 N+1 系列http://mvvmcross.wordpress.com/ 中,他使用消息服务大概是为了避免这种情况。

因此,一种可能的解决方案可能是发布带有最新项目列表的消息,并让 ViewModel 订阅该消息并通过将消息内容与其进行比较来在 UI 线程上更新其自己的 ObservableCollection。但这似乎有点笨拙。

任何有关实现此功能的最佳方法的建议都将不胜感激 - 谢谢。

【问题讨论】:

    标签: mvvm windows-phone-8 xamarin.ios mvvmcross


    【解决方案1】:

    INotifyCollectionChanged 必须在 UI 线程上调用的原始要求实际上来自 Windows 控件根据添加/删除/移动/替换/重置通知更新的同步方式。

    当然,这种同步更新是完全明智的——当另一个线程正在积极地改变它时,更新 UI 显示会非常困难。

    .Net 4.5 中有“新”变化,这可能意味着未来会更好……但总的来说,这些对我来说看起来相当复杂 - 请参阅 https://*.com/a/14602121/373321


    我知道的处理方法与您帖子中概述的方法基本相同:

    A.将ObservableCollection 保留在服务/模型层中,并将那里的所有事件编组到 UI 线程上——这可以使用任何继承自 MvxMainThreadDispatchingObject 的类来实现——或者可以通过调用 MvxMainThreadDispatcher.Instance.RequestMainThreadAction(action) 来完成

    虽然不幸的是,这意味着您的服务/模型确实具有一些线程知识,但这种方法可以很好地用于整体应用体验。

    B.在ViewModel 中制作集合的副本 - 通过一些弱引用类型机制对其进行更新

    • 例如通过向它发送消息来告诉它已添加、删除、替换或移动(或完全重置)的内容 - 请注意,要使其正常工作,消息以正确的顺序到达很重要!

    • 或例如允许从服务/模型层发送快照

    选择哪一个取决于:

    • 集合的频率、类型和大小发生变化 - 例如无论您是否只是偶尔获得单行更新,是否经常收到大量更改,或者您是否主要看到复杂的更改组(就 UI 而言,基本上是 Resets
    • UI 中所需的动画级别 - 例如添加/删除的项目应该动画进/出吗?如果不需要动画,则有时将整个列表替换为新快照会比计算增量更改更容易。
    • 集合本身的大小 - 显然复制大型集合会导致内存不足问题
    • 集合所需的持久性 - 如果需要持久性,则 ObservableCollection 本身可能不合适,您可能需要使用自定义 INotifyCollectionChanged 实现(如 the MyCustomList samples

    我个人通常在应用程序中选择 (A) 方法 - 但它确实取决于情况以及集合的特征及其变化​​。

    请注意,虽然这绝对是一个mvvm 问题,但根本问题是一个独立于数据绑定的问题 - 当列表本身在后台线程上发生变化时,如何更新列表的屏幕显示?

    【讨论】:

    • 新的EnableCollectionSynchronization 看起来很有趣,但你是对的——它看起来确实很复杂,实现锁定与编组事件一样麻烦。好的,所以它看起来毕竟是可行的,我猜如果 ViewModel 没有处理它的任何事件,那么它不会通过被服务引用来保持活动状态。因此,再次感谢您提供全面的答案以及有关如何从服务返回主线程的提示。
    • 我试过这个MvxMainThreadDispatcher.Instance.RequestMainThreadAction 但它没有帮助:( 还有什么可以做的?