6

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

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

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

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

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

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

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

4

1 回答 1

13

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

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

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


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

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

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

B. 复制集合中的副本ViewModel- 通过一些弱引用类型机制更新它

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

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

选择哪一个取决于:

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

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

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

于 2013-08-27T15:27:15.770 回答