在MvvmCross解决方案中,我有一个单例服务类,它从Web服务获取项目并更新公共ObservableCollection。它每五秒执行一次,可以添加或删除项目或更改其属性。
我还有一个ViewModel,它有一个公共属性,设置为Service的ObservableCollection。视图绑定到ObservableCollection,以便在添加,删除或更改项目时,视图应更新以显示此内容。
然而,正如预期的那样,我得到了一个线程异常,因为ObservableCollection正在由Main / UI之外的线程更新,因此绑定无法更新UI。
在服务中我没有InvokeOnMainThread
调用随时可用,因此在更新ObservableCollection时没有明显的跨平台方法可以返回主线程。此外,这样做似乎是错误的 - 服务不应该关注UI问题(而ViewModel可以)。
我也有点担心从服务中暴露事件,以防这会导致ViewModel不被垃圾收集。我注意到在@ slodge的N + 1系列http://mvvmcross.wordpress.com/中,他正在使用一种消息服务,大概是为了避免这种情况。
因此,一个可能的解决方案可能是发布带有最新项目列表的消息,并且ViewModel可以通过比较消息内容来订阅消息并在UI线程上更新自己的ObservableCollection。但这似乎有点笨拙。
任何有关实施此方法的最佳方式的建议都将受到赞赏 - 谢谢。
答案 0 :(得分:13)
必须在UI线程上调用INotifyCollectionChanged
的原始要求实际上来自Windows控制基于添加/删除/移动/替换/重置通知更新的同步方式。
这种同步更新当然是完全合理的 - 当另一个线程正在积极地更改它时,更新UI显示将非常困难。
.Net 4.5中存在“新”变化,这可能意味着未来更加美好......但总体而言,这看起来相当复杂 - 请参阅https://stackoverflow.com/a/14602121/373321
我知道处理此问题的方法与您帖子中列出的方法基本相同:
一个。保留服务/模型层中的ObservableCollection
并将其中的所有事件编组到UI线程上 - 这可以使用任何继承自MvxMainThreadDispatchingObject
的类 - 或者可以通过调用MvxMainThreadDispatcher.Instance.RequestMainThreadAction(action)
<来完成/ p>
虽然不幸的是,这意味着您的服务/模型确实具有一些线程知识,但这种方法可以很好地适用于整体应用体验。
B中。在ViewModel
中制作集合的副本 - 通过一些弱引用类型机制更新它
e.g。通过发送消息告诉它添加,删除,替换或移动(或完全重置)的内容 - 请注意,为了使其正常工作,消息以正确的顺序到达非常重要!
或者例如允许从服务/模型层发送快照
选择以下哪项取决于:
Resets
)ObservableCollection
本身可能不合适,您可能需要使用自定义INotifyCollectionChanged
实现(如the MyCustomList samples)我个人通常在应用程序中选择(A)方法 - 但它确实取决于情况以及集合的特征及其变化。
请注意,虽然这绝对是一个mvvm
问题,但基本问题是一个独立于数据绑定的问题 - 当列表本身在后台线程上更改时,如何更新列表的屏幕显示?