4

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

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
    }
}

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>( );
    }

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

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!

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. Idea how Not to subscribe to messages on the ViewModel's ctor.
  2. Guidance about how and when to correctly dispose the ViewModel.
  3. Explanation on why the constructor is being called again, and how to avoid that.
  4. A complete different approach to ViewModel information messaging.

Thanks in advance for you help!

Edit: 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.

Another Edit: 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.

Community
  • 1
  • 1
Liel
  • 2,407
  • 4
  • 20
  • 39

1 Answers1

3

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

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.

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

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 removes your first Page (and it's ViewModel) from memory - I guess this might happen if your app is tombstoned or if you are using a custom RootFrame - but I don't expect this to happen by default.
  • you somehow null the ViewModel (DataContext) in your first Page

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


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?


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.

Community
  • 1
  • 1
Stuart
  • 66,722
  • 7
  • 114
  • 165
  • Thanks for your answer @Stuart! I am not facing tomb-stoning nor using a custom RootFrame. Also, I am not doing anything fancy such as nulling my ViewModel. Actually, I verified that the same behavior exists with your tutorial named `N-05-MultiPage`. I simply added a `ctor` to `SecondViewModel`, and I hit a breakpoint inside it after each BACK+Renavigate. Now, I don't mind recreating View+ViewModel each navigation, but I do want to make sure that I get a consistent cross-platform behavior. Do you have any suggestions for me? Thanks! – Liel Jun 03 '13 at 18:13
  • Are you talking about getting a navigate on the first view model? or on the second? If its on the first, then I would not expect that - that's what my answer was about. If it's on the second, then yes, you are doing a new navigation to a new page so of course you get a new view and a new viewmodel - and that will give you "consistent cross-platform behavior" – Stuart Jun 04 '13 at 17:35
  • I am trying my best to understand your answers, please bear with me :). I am talking about this scenario: 1. from first view model call `ShowViewModel()`. 2. now call `ShowViewModel()` (or hit the back button, doesn't matter). 3. now call `ShowViewModel()`. In each of the three steps above, the `ctor` is being called. So are you saying that this is the expected behavior? – Liel Jun 04 '13 at 17:41
  • and if it is, how and when would you recommend me to do `Subscribe` to the messenger events? – Liel Jun 04 '13 at 17:43
  • if you need to pass a parameter during a navigation, you don't have to use a messenger - see https://github.com/slodge/MvvmCross-Tutorials/tree/master/Navigation or http://slodge.blogspot.co.uk/2013/04/n5-some-first-icommands-and-multiple.html, http://slodge.blogspot.co.uk/2013/03/v3-new-viewmodel-lifecycle.html and http://stackoverflow.com/questions/10658913/what-is-the-best-way-to-pass-objects-to-navigated-to-viewmodel-in-mvvmcross or http://stackoverflow.com/questions/10192505/passing-on-variables-from-viewmodel-to-another-view-mvvmcross/ (RequestNavigate has been replaced by ShowViewModel) – Stuart Jun 04 '13 at 18:01
  • I also don't see the same behaviour as you - if in step 2 you hit Back then this should not cause a new FirstViewModel to be constructed. I honestly think what you need to understand is when WP creates a new Page. Whenever a new page is created then a new ViewModel will be located - and by default this will cause a new ViewModel to be constructed. – Stuart Jun 04 '13 at 18:05
  • That is true, and indeed I can use navigation parameters for VM to VM communication. I will change my code properly. However, I now no longer see any reason/possibility to `Subscribe` to messenger events inside a VM, as long as I can't `Unsubsribe` on VM deletion, or could I? – Liel Jun 04 '13 at 18:10
  • Sorry - the questions in comments are making no sense to me now... maybe try a new simple question with code **just on this point** (whatever it is!) - I will **try** to understand. – Stuart Jun 04 '13 at 18:14