Use NavigateAsync in Prism.Forms with an already existant ViewModel

328 Views Asked by At

Scenario: I have view, view model and model for PickList and PickLine. The PickListViewModel contains an ObservableCollection<PickLineViewModel> and the PickList model contains a List<PickLine>. My PickList page contains a ListView which is bound to the ObservableCollection<PickLineViewModel> and if a line is tapped NavigateAsync is called to navigate to the tapped PickLine.

Normally, when I call NavigateAsync Prism navigates to the page, locates the view model, creates an instance of it and binds this instance to the view. But in this case the view model instance that should be bound to the page is already existant (as an element of my ObservableCollection) and I don't want the Prism ViewModelLocator to create a new instance, due to the fact that it then would have to get data from web service and I try to keep the number of web service calls as low as possible.

Also I can't use models in the ObservableCollection because the view model contains properties which are only used for UI purposes so these properties should definitely not be part of the model, but the UI properties I'm talking about are needed in the PickList page and in the PickLine page.

Tl;dr: Is there any way in Prism.Forms to provide a view model instance on navigating to a page that will than be bound to it?

2

There are 2 best solutions below

3
Dan Siegel On

Your problem is that you are confusing what a Model is and what a ViewModel is. In this case what you should have is:

  • PickLine (Model)
  • PickLineView (View)
  • PickLineViewModel (ViewModel)
  • PickLineListView (View)
  • PickLineListViewModel (ViewModel)
    • Containing your ObservableCollection of PickLine not PickLineViewModel

Without seeing your precise code, I'm going to take a guess from experience here that your code probably looks something like the following in principal:

public ObservableCollection<PickLineViewModel> Items { get; set; }
public DelegateCommand<PickLineViewModel> ItemTappedCommand { get; }
public void OnNavigatedTo(INavigationAware parameters)
{
    var picks = _dataService.GetPickLines();
    Items = new ObservableCollection<PickLineViewModel>(
              picks.Select(x => new PickLineViewModel
              {
                  Model = x,
                  ItemTappedCommand = ItemTappedCommand
              });
}

I see code like the above a lot from people who confuse the difference between a Model and ViewModel, and often from people who don't understand how to properly bind back to the ViewModel from something like a Cell in a ListView.

What you should have instead is code that is more like:

public ObservableCollection<PickLine> Items { get; set; }
public DelegateCommand<PickLine> ItemTappedCommand { get; }
public void OnNavigatedTo(INavigationAware parameters)
{
    var picks = _dataService.GetPickLines();
    Items = new ObservableCollection<PickLine>(picks);
}
5
Haukinger On

Is there any way in Prism.Forms to provide a view model instance on navigating to a page that will than be bound to it?

No, as far as I know, Prism Forms does not provide view model-first navigation (as opposed to Prism on WPF which does).

You should be able to work around this, though, by passing your existing view model as parameter and making the ViewModelLocator-created view model an "empty shell" that redirects everything to the actual view model.