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

时间:2013-06-01 14:28:21

标签: c# windows-phone-8 mvvmcross

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

由于我想在ViewModel之间共享信息,我使用内置的MvxMessenger在ViewModel的构造函数中注册通知。
让我们假设一条名为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()处理程序被触发两次!

我发现了this类似的帖子,讨论了如何正确处理ViewModel的问题,
但它不能直接解决我的问题。

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

  1. 想法如何不订阅ViewModel的ctor上的消息。
  2. 关于如何以及何时正确处理ViewModel的指导。
  3. 解释为什么再次调用构造函数,以及如何避免这种情况。
  4. 一种完全不同的ViewModel信息消息传递方法。
  5. 先谢谢你的帮助!

    编辑: 我找到this SO答案,它基本上回答了上面列表中的第3项。 不过,我想知道我应该采取什么方法处理信使问题。

    另一个编辑: 我验证了MvvmCross教程N-05-MultiPage存在相同的行为。我只是向SecondViewModel添加了一个ctor,并且在每次BACK + Renavigate之后我在它内部打了一个断点。

1 个答案:

答案 0 :(得分:2)

  

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

ctor未在同一对象上调用两次 - 而是每次都会创建一个新的View和一个新的ViewModel

默认情况下, 期望在每个平台上的每个转发导航中创建新的ViewModel。

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

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

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


我个人建议你深入了解为什么你会看到在Back期间创建新的ViewModel,但是如果你只想快速修复,那么你可以看看覆盖MvvmCross中的ViewModelLocator - 参见MvvmCross: Does ShowViewModel always construct new instances?


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