ViewModel'的构造函数在导航时再次被调用,因此再次订阅了信使订阅

本文关键字:信使 调用 构造函数 导航 ViewModel | 更新日期: 2023-09-27 18:16:43

我正在使用MvvmCross框架构建一个跨平台的移动应用程序。

因为我想在ViewModels之间共享信息,我在ViewModel的构造函数中注册通知,使用内置的MvxMessenger
让我们假设一个名为ShowAdsMsg的消息,然后ViewModel看起来如下:

public class AdsViewModel : BaseLookersViewModel, IAdsViewModel
{
    private MvxSubscriptionToken _showAdsMsgToken;
    public AdsViewModel()
    {
        _showAdsMsgToken = MvxMessenger.Subscribe<ShowAdsMsg>(message => onShowAdsNavigation(), MvxReference.Weak);
        MyMessenger.PublishLastMessage();
    }
    private void onShowAdsNavigation()
    {
        //Do Stuff
    }
}

关于MyMessenger的事情:
到ViewModel的实际导航是从MainViewModel执行的。
由于在导航本身的那一刻AdsViewModel还不存在,从MainViewModel发布的消息无法到达它。
所以,我的想法是天真地"记住"消息,并在新的ViewModel准备好时发布它。所以现在MainViewModel的导航调用看起来像这样:

    private void navigate()
    {
        MyMessenger.RememberMessage(new ShowAdsMsg(this));
        ShowViewModel<AdsViewModel>( );
    }

我现在能够导航到ViewModel,并且所有的通知都被成功捕获。

然而


当我按下设备上的BACK按钮并重新导航到相同的ViewModel时,
构造函数再次被调用,因此消息订阅再次发生。
结果,当消息到达时,onShowAdsNavigation()处理程序被触发两次!

我发现了这个类似的帖子,讨论如何正确处理ViewModel的问题,
但是它没有直接解决我的问题。

我需要的是一个解决方案。它可以是下列任意一种:

  1. 关于如何在ViewModel的actor上不订阅消息的想法。
  2. 关于如何以及何时正确处置ViewModel的指导。
  3. 解释构造函数为什么会被再次调用,以及如何避免这种情况。
  4. 一个完全不同的ViewModel信息传递方法。

提前感谢您的帮助!

编辑:我找到了这个SO答案,它基本上回答了上面列表中的第3个问题。但是,我在想我应该采取什么方法来解决信使的问题。

另一个编辑:我验证了同样的行为存在于MvvmCross教程N-05-MultiPage。我只是在SecondViewModel中添加了一个actor,并在每次BACK+ renavate之后在其中命中一个断点。

ViewModel'的构造函数在导航时再次被调用,因此再次订阅了信使订阅

解释构造函数为什么会被再次调用,以及如何避免这种情况。

ctor不会在同一个对象上被调用两次——相反,可能发生的情况是每次都创建一个新的View和一个新的ViewModel

默认情况下,我期望在每个平台上的每个正向导航上创建一个新的ViewModel。

默认情况下,我将**不期望在WindowsPhone的后退按钮期间发生这种情况-它不会在我的测试用例中发生-但它可能发生,如果:

  • WindowsPhone从内存中删除你的第一页(和它的ViewModel) -我想这可能会发生,如果你的应用程序是墓碑,或者如果你使用自定义RootFrame -但我不希望这在默认情况下发生。
  • 你以某种方式在你的第一页null ViewModel (DataContext)

如果没有看到更多的代码,我无法猜测更多的为什么会发生这种情况。


我个人建议你更深入地了解为什么你在Back期间看到新的ViewModels创建,但如果你只是想快速修复,那么你可以看看在MvvmCross中重写ViewModelLocator -参见MvvmCross: ShowViewModel总是构造新的实例吗?


请注意,在WindowsStore上,我希望发生这种情况- WindowsStore默认情况下不会在内存中保存来自backstack的页面-但是如果需要,您可以通过设置NavigationCacheMode = NavigationCacheMode.Enabled;来覆盖这一点。