导航时再次调用 ViewModel 的构造函数,因此再次订阅了 Messenger 订阅 [英] ViewModel's constructor is being called again on navigation, and so messenger subscriptions are being subscribed again

查看:25
本文介绍了导航时再次调用 ViewModel 的构造函数,因此再次订阅了 Messenger 订阅的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 MvvmCross 框架构建跨平台移动应用程序.

I am building a cross-platform mobile application using MvvmCross framework.

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

Since I would like to share information between ViewModels, I am registering notifications inside the ViewModel's constructor, using the built in MvxMessenger.
Let's assume a message named ShowAdsMsg, and then the ViewModel looks as follows:

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 的导航调用看起来像这样:

About the MyMessenger thing:
The actual navigation to the ViewModel is performed from MainViewModel.
Since that at the very moment of the navigation itself the AdsViewModel does not exist yet, messages published from the MainViewModel cannot reach it.
So, my idea was to naively "remember" the message and publish it when the new ViewModel is ready.
So now the navigation call from the MainViewModel looks like that:

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

我现在可以导航到 ViewModel,并且成功捕获了所有通知.

I am now able to navigate to the ViewModel, and all the notifications are successfully caught.

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

However...
When I press the BACK button on the device and re-navigate to the same ViewModel,
The constructor is being called again, and so the message subscription re-occur.
As a result, when a message arrives the onShowAdsNavigation() handler is being fired twice!

我发现了这篇类似的帖子,讨论了如何正确处理 ViewModel,
但它不包含我的问题的直接解决方案.

I found this similar post, discussing the question of how to properly dispose a ViewModel,
but it does not contain a direct solution to my problem.

我需要的是一个解决方案.它可以是以下之一:

What I need is a solution. It can be either one of the following:

  1. 了解如何不订阅 ViewModel 构造函数上的消息.
  2. 有关如何以及何时正确处置 ViewModel 的指南.
  3. 解释为何再次调用构造函数,以及如何避免这种情况.
  4. 一种完全不同的 ViewModel 信息消息传递方法.

预先感谢您的帮助!

我找到了 this SO Answer,它基本上回答了第 3 项上面列出.不过,我想知道我应该对 Messenger 问题采取什么方法.

I found this SO Answer, which basically answers item number 3 in the list above. Still, I am wondering what approach should I take regarding the messenger issue.

另一个我验证了 MvvmCross 教程中存在相同的行为 N-05-MultiPage.我只是在 SecondViewModel 中添加了一个 ctor,然后在每次 BACK+Renavigate 之后在里面打了一个断点.

Another I verified that the same behavior exists with MvvmCross tutorial N-05-MultiPage. I simply added a ctor to SecondViewModel, and I hit a breakpoint inside it after each BACK+Renavigate.

推荐答案

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

Explanation on why the constructor is being called again, and how to avoid that.

ctor 不会在同一个对象上调用两次 - 相反,可能会创建一个新的 View 和一个新的 ViewModel每次.

The ctor is not called twice on the same object - instead what might happen is that a new View and a new ViewModel are created each time.

默认情况下,我希望在每个平台的每个前向导航上创建一个新的 ViewModel.

By default I would expect a new ViewModel to be created on every forwards navigation on every platform.

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

By default I would **not expect this to happen during a back button on WindowsPhone - it doesn't happen here for my test cases - but it could happen if:

  • WindowsPhone 会从内存中删除您的第一个页面(以及它的 ViewModel) - 我想如果您的应用被逻辑删除或者您使用自定义 RootFrame 可能会发生这种情况 - 但我不希望默认情况下会发生这种情况.
  • 您以某种方式将第一个页面中的 ViewModel (DataContext) 设为 null

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

Without seeing more of your code I can't guess any more about why this might happen.

我个人建议您更深入地了解为什么您会看到在 Back 期间创建的新 ViewModel,但如果您只想快速修复,那么您可以查看在 MvvmCross 中覆盖 ViewModelLocator - 参见 MvvmCross:ShowViewModel 是否总是构建新实例?

I'd personally recommend you look deeper at why you are seeing new ViewModels created during Back, but if you just want a quick fix, then you could look at overriding the ViewModelLocator within MvvmCross - see MvvmCross: Does ShowViewModel always construct new instances?

请注意,在 WindowsStore 上,我希望这种情况会发生 - 默认情况下,WindowsStore 不会在内存中保留后台堆栈中的页面 - 但您可以通过设置 NavigationCacheMode = NavigationCacheMode.Enabled; 来覆盖它,如果你需要.

Note that on WindowsStore, I would expect this to happen - WindowsStore doesn't hold Pages from the backstack in memory by default - but you can overriding this by setting NavigationCacheMode = NavigationCacheMode.Enabled; if you need to.

这篇关于导航时再次调用 ViewModel 的构造函数,因此再次订阅了 Messenger 订阅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆