4

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

因为我想在 ViewModel 之间共享信息,所以我在 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. 由于在导航本身还不存在
的那一刻,从 发布的消息无法到达它。 所以,我的想法是天真地“记住”消息并在新的 ViewModel 准备好时发布它。 所以现在导航调用看起来像这样:AdsViewModelMainViewModel

MainViewModel

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

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

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

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

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

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

提前感谢您的帮助!

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

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

4

1 回答 1

3

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

不会在ctor同一个对象上调用两次 - 而可能发生的是每次都会创建一个新的View和一个新的。ViewModel

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

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

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

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


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


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

于 2013-06-03T09:34:28.487 回答