【问题标题】:MvvmCross: Best way to raise event in ViewModel?MvvmCross:在 ViewModel 中引发事件的最佳方式?
【发布时间】:2016-09-04 09:18:58
【问题描述】:

我目前有一个视图(Android 片段)和一个对应的 ViewModel。我现在想在 ViewModel 中引发一个 View 可以订阅的事件。

存档的最佳方式是什么?我听说一个常规的 C# 事件(委托)会导致内存泄漏?这是 WeakSubscribe 功能的原因吗?如何将其绑定到事件?

【问题讨论】:

  • 这个事件在处理的时候在做什么?属性更改通知会起作用吗?
  • ViewModel 管理一个项目列表(它们是 Sub-ViewModels),一旦用户点击其中一个子项目,ViewModel 应该通知视图该子项目被点击。我还需要点击哪个项目的信息。
  • 你对视图中的这些信息做了什么?
  • 我正在显示一个 Toast 以通知用户该项目已成功添加到收藏列表。
  • 我个人不会添加事件并在视图中处理它 - 相反,我会创建一个通过接口公开的通知服务并在视图模型中调用它,并在 android 代码中实现表示敬酒。这样它就更易于单元测试,并且更容易跨平台 - 只需编写服务的 iOS 实现,以您想要的任何方式显示通知。

标签: c# xamarin mvvmcross


【解决方案1】:

为了防止视图中的内存泄漏,您订阅的每个事件都需要取消订阅,无论是使用WeakSubscribe 还是像往常一样订阅事件。

一个常见的场景是订阅:

  • 安卓OnResume()
  • iOS ViewWillAppear()

然后在以下位置处理订阅:

  • 安卓OnPause()
  • iOS ViewWillDisappear()

弱订阅

如果您想“监听” ViewModel 属性的变化,WeakSubscribe 会派上用场:

private IDisposable _selectedItemToken;

_selectedItemToken = ViewModel.WeakSubscribe(() => 
    ViewModel.SelectedItem, (sender, eventArgs) => {
        // do something
});

请注意WeakSubscribe() 返回一个MvxWeakEventSubscription,它也是IDisposable。您需要在视图中保存对该订阅的引用,并在不再需要该视图时将其丢弃。 保留该引用有两个原因:

  1. 您可以稍后处理它
  2. 如果您不保留它,您的 lambda 事件处理程序可能无法始终正常工作

稍后...

_selectedItemToken?.Dispose();

普通事件订阅

如果您只需要订阅 ViewModel 中的另一种事件(不是属性更改),您实际上不需要WeakSubscribe。您可以像处理任何对象一样向 ViewModel 添加事件侦听器。

ViewModel.AnEvent += YourDelegate;

稍后...

ViewModel.AnEvent -= YourDelegate;

不要忘记最后一步。这将防止内存泄漏。正如我所说,Android OnPause() 和 iOS ViewWillDisappear() 是这样做的好地方。

这样您的 ViewModel 不会在视图被处理时卡在内存中,因此您的视图可以正确地被垃圾收集。

【讨论】:

  • 这看起来很有希望。如果我问你一个关于如何使用自定义委托类型订阅事件的简单示例,你介意吗?我遇到了各种各样的麻烦。
  • 你已经尝试了什么?遇到了什么样的麻烦?
  • 我使用以下 sn-p 进行了尝试:_subscription = typeof(TViewModel) .GetEvent("ItemAdded") .WeakSubscribe(this.ViewModel, OnItemAdded); 但我收到一条错误消息,提示“无法转换实例参数类型“System.Reflection.EventInfo”[mscorlib, Version=2.0.5.0]到“System.Reflection.EventInfo”[mscorlib,Version=4.0.0.0]。这可能不相关,但我不完全确定。
  • 我在答案中添加了一个示例
  • 我现在明白了。我完全误解了你的问题。我会相应地更新它
【解决方案2】:

如果您订阅临时对象中的事件并且在释放临时对象之前未取消订阅,则可能会造成泄漏。在您的情况下,这种可能性很小,因为视图模型很可能只会创建一次。

由于您使用的是 mvvm,因此事件的替代方案是 Messenger,您可以在流行的 mvvm 框架(如 MvvmLight 或 MvvmCross)中找到它的实现。这为您提供了真正解耦的事件,因为您只需要知道消息的格式并且不需要知道有关发送者的任何信息(在您的情况下是 ViewModel)。使用 messenger,您只需订阅消息类型,发件人可以在应用程序中的任何位置。

【讨论】:

  • 无需将视图与 ViewModel 分离。反之亦然(ViewModel 不应该知道 View)。当您可以直接订阅 ViewModel 更改时,为什么要使用信使?
猜你喜欢
  • 2023-03-07
  • 1970-01-01
  • 2014-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
相关资源
最近更新 更多