Who should create view model instances in MvvmCros

2019-06-05 04:31发布

问题:

Just to make it clear: I know MvvmCross is very flexible about where and how view models can be created. My question is more about proper separation of concerns, to simplify design of complex cross-platform applications.

Consider we have an app with customer list and customer details. On iPad and Surface the list and details are shown on the same page, but on smaller devices customer list and details for a selected customer are split between separate pages. So we have a PCL with CustomerListViewModel and CustomerDetailsViewModel. Now how should we manage view model lifetime from within the portable class library?

I originally did it using code in CustomerListViewModel implementation that looks like this:

public ICommand SelectCustomerCommand
{
    get { return new MvxCommand(SelectCustomer); }
}

public void SelectCustomer()
{
    if (formFactor == FormFactor.Phone)
    {
        ShowViewModel<CustomerDetailsViewModel>(new CustomerDetailsViewModel.NavObject(this.SelectedCustomer));
    }
    else
    {
        this.CustomerDetails = new CustomerDetailsViewModel(this.SelectedCustomer);
    }
}

What is essential here is that we either call ShowViewModel that in turns asks a presenter to construct a CustomerDetailsViewModel object and render it in a new page or explicitly create an instance of CustomerDetailsViewModel and bind it to CustomerDetails.

After having seen episodes 32 and 42 of N+1 MvvmCross video series I am not quire sure this is the right way to do it. Of course it works, but should a view model care about instantiation details of other view model?

My second thought was to refactor this code and put this logic into a presenter, so I can simply write in CustomerListViewModel implementation:

public void SelectCustomer()
{
    ShowViewModel<CustomerDetailsViewModel>(new CustomerDetailsViewModel.NavObject(this.SelectedCustomer));
}

... and presenter will do the rest inside the code triggered by ShowViewModel call. However, in the episode 42 it's shown how to control view model lifetime right from the associated view:

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);
    VisibleViewModel.IsVisible(false);
    if (e.NavigationMode == NavigationMode.Back)
        KillableViewModel.KillMe();
}

But if a view model lifetime is controlled by a presenter, shouldn't we try to place KillMe call inside presenter's logic? I know so small piece of code doesn't make much difference but couldn't this be an advantage to put it in presenter's class and reduce code-behind?

回答1:

Definitely the ViewModel should not handle anything in regards to view (screen).

One quick idea I have is use a custom presenter which is able to create views based on the ShowViewModel<> requests.

The custom presenter is a view responsibility so you can test for screen orientation.

http://slodge.blogspot.co.uk/2013/06/presenter-roundup.html



回答2:

For the second part of this question:

But if a view model lifetime is controlled by a presenter, shouldn't we try to place KillMe call inside presenter's logic? I know so small piece of code doesn't make much difference but couldn't this be an advantage to put it in presenter's class and reduce code-behind?

Currently view model presentation is orchestrated by the presenter - it gets ViewModelRequest objects and decides what to do with them.

However, it doesn't normally create the ViewModels - instead:

  • the presenter normally creates/shows a View
  • then the View creates (or locates) a ViewModel as part of the OnCreate, ViewDidLoad or OnNavigatedTo handling.

And so I don't think a ViewModel's lifetime is normally "controlled by a presenter" - instead I think a ViewModel is a "Model for a View" - so it's lifetime is "controlled by its view".

For the occasions where you need shutdown/dispose/killMe logic in your ViewModel, if you wanted to move that logic back inside the presenter - or into some other object - then you definitely could do so if you wanted to - it's your app and the app is king.

However, in these cases I suspect you would need the presenter to get some kind of notification from the View - as often the presenter doesn't know when Views are removed (when a modal is dismissed, when a Back button is pressed, when Android clears up stack views to save memory, etc).

As another way of thinking about this, if the presenter were renamed as INavigationService then what roles do you want that INavigationService to own within your app?