1

I'm trying to follow the Model–View–ViewModel pattern in a Xamarin application and am wondering how the View should be able to change when a given ViewModel is finished. Below I have listed the ideas I have come up with doing what I need.

Creating the new View directly in the ViewModel.

Currently, each ViewModel explicitly sets the Application's MainPage to a different View when the ViewModel is done; I believe this violates the MVVM pattern because while each ViewModel doesn't know about its own View it does know about the View that follows it. Here is an example inside a ViewModel:

private async Task ExecuteSubmitCommand()
{
  // Do stuff here.

  // Go to a different page.
  Application.Current.MainPage = new Views.NewUploadWizardPage(this.imageBatch, appInfo);
}

While this is easy to do I feel like it is not a best practice and it makes testing the ViewModel very difficult.

Sending a message from the ViewModel to signal that it is finished.

With this approach, there could be a "view controller" object that persists the lifetime of the program and handles the creation of new Views. This controller object could subscribe to messages which are sent from the ViewModels to know when to switch Views. Example:

Controller:

private void Method1()
{
  Application.Current.MainPage = new ImageBatchEntryView();
  MessagingCenter.Subscribe<ImageBatchEntryViewModel>(this, "Finished", async (vm, tuple) => await Method2(vm, tuple));
}

private async Task Method2(ImageBatchEntryViewModel vm, Tuple<ImageBatch, AppInfo> tuple)
{
  MessagingCenter.Unsubscribe<ImageBatchEntryViewModel>(this, "Finished");

  Application.Current.MainPage = new Views.NewUploadWizardPage(tuple.Item1, tuple.Item2);
  MessagingCenter.Subscribe<NewUploadWizardViewModel>(this, "Finished", async (vm, something) => await Method3(vm, something));
}

ViewModel:

private async Task ExecuteSubmitCommand()
{
  // Do stuff here.

  // Send message.
  MessagingCenter.Send(this, "Finished", new Tuple<ImageBatch, AppInfo>(this.imageBatch, appInfo));
}

Raising an event in the ViewModel to signal that is is finished.

This is similar to the message approach but uses events instead. The "view controller" object could just subscribe to an event in a ViewModel and change the View when that event is raised.

Other Ideas?

Is there an approach that's considered best practice for having ViewModels initiating View changes?

Aaron T
  • 121
  • 4

1 Answers1

1

Create a navigation service interface in the VM layer. It's up to the UI layer to actually implement this however. The VMs tell the navigation service to move to the next page or go back. It's similar to messaging, but the focus is actually navigation.

Look at a framework like MvvmLight, they handle this stuff for you. MvvmLight also includes a messaging component as well.

Andy
  • 2,003
  • 16
  • 22
  • So, the VMs would have this navigation service injected and just call a method on it when they're done? That seems simple enough, but how does the VM pass any parameters? Perhaps instead of explicitly passing parameters when finished the VMs can just make them publicly accessible members? – Aaron T Apr 15 '19 at 18:37
  • @aaront Yes the nav service is injected and the VM just calls its methods. If you look at MvvmLight there's a NavigateTo overload that lets you pass a parameter. See http://www.mvvmlight.net/doc/nav1.cshtml – Andy Apr 16 '19 at 00:23