WickedW
WickedW

Reputation: 2601

Set Up INavigation with Xamarin.Forms and MVVMLight IOC

I am beginning using MVVMLight with Xamarin.Forms and need some help to get the IOC set up to create my first ContentPage in App.GetMainPage.

My ViewModels have constructors like so -

public class NewsType1ViewModel : ViewModelBase
{
    private readonly IContentService contentService;
    private readonly INavigation navigation;

    public List<ContentModel> ContentModels { get; set; }

    public NewsType1ViewModel (INavigation navigation, IContentService contentService)
    {
        this.contentService = contentService;
        this.navigation = navigation;
    }

My ContentPages have constructors like so -

public partial class NewsType1CP : ContentPage
{
    private NewsType1ViewModel vm;

    public NewsType1CP (NewsType1ViewModel vm)
    {
        InitializeComponent ();

I am using a ViewModelLocator class as follows -

public class ViewModelLocator
{
    public ViewModelLocator ()
    {
        ServiceLocator.SetLocatorProvider (() => SimpleIoc.Default);

        // Services 
        if (App.StubMode) {
            SimpleIoc.Default.Register<IContentService, ContentServiceStub> ();
        } else {
            SimpleIoc.Default.Register<IContentService, ContentService> ();
        }

        // How do I wire up INavigation?
        // I could not just register default NavigationPage() as it has 2 
        // possible constructors so tried [PreferredConstructor] on my derived class
        SimpleIoc.Default.Register<INavigation, AppNavigationPage> ();

        // View Models
        SimpleIoc.Default.Register<NewsType1ViewModel> ();
        SimpleIoc.Default.Register<NewsDetailsViewModel> ();
    }

    public NewsType1ViewModel NewsType1ViewModel {
        get {
            return ServiceLocator.Current.GetInstance<NewsType1ViewModel> ();
        }
    }

    public NewsDetailsViewModel NewsDetailsViewModel {
        get {
            return ServiceLocator.Current.GetInstance<NewsDetailsViewModel> ();
        }
    }

    public static void Cleanup ()
    {
        // TODO Clear the ViewModels
    }
}

public class AppNavigationPage : NavigationPage
{
    [PreferredConstructor]
    public AppNavigationPage ()
    {

    }
}

My App.cs is "in progress" as follows -

public static class App
{
    public static AppNavigationPage Nav;
    public static ViewModelLocator Locator = new ViewModelLocator ();

    public static bool StubMode = true;

    public static Page GetMainPage ()
    {   
        try {
            // Want to return a Nav with NewsType1CP as the starting Page
            NewsType1CP newsType1CP = new NewsType1CP(ServiceLocator.Current.GetInstance<NewsType1ViewModel> ());
            Nav.PushAsync (newsType1CP);
            return Nav;
        } catch (Exception ex) {
            //
            Exception baseexception = ex.GetBaseException ();
            Debug.WriteLine (baseexception.Message);
        }

        return null;
    }
}

My latest exception is -

Cannot cast from source type to destination type.

Am I barking up the wrong tree trying to supply an INavigation to each of my ViewModels like so?

Update: After a couple of answers showing other ways of controlling the Navigation in Xamarin Forms, I think it would help if someone could clarify why attempting constructor injection for Navigation is not such a good thing.

I think my example is a bit confusing with AppNavigationPage as a static, ideally I would like this to be in the IOC also, so I could just call return ServiceLocator.Current.GetInstance< AppNavigationPage >(), but I had a go with various factory methods and am in the middle of debugging this, so the code is obviously half baked ...

Upvotes: 0

Views: 5181

Answers (3)

Rui Marinho
Rui Marinho

Reputation: 1712

Oh ok, when looking better at your code i maybe spotted the problem:

SimpleIoc.Default.Register<INavigation, AppNavigationPage> ();

Shoult it be:

SimpleIoc.Default.Register<INavigation, AppNavigationPage.Navigation> ();

Upvotes: 0

Rui Marinho
Rui Marinho

Reputation: 1712

If you want a ready to use solution, instead of MVVM Light, you could try to use Xamarin Forms Labs ViewModel base, it injects a Navigation propety in your ViewModel:

that way you cold do something like this:

 public Command NavigateToViewModel 
{
    get
    {
        return navigateToViewModel ?? (navigateToViewModel = new Command(
                                                                   async () => await Navigation.PushAsync<NewPageViewModel>(),
                                                                   () => true));
    }
}

Upvotes: 2

Chase Florell
Chase Florell

Reputation: 47407

I'm not using MvvmLight, but I can tell you that yes, you're barking up the wrong tree in trying to supply an INavigation to each ViewModel.

The easiest way to achieve what you're trying to do is to use a public static on your App.

public static INavigation Navigation
{
    get;
    set;
}
public static Page GetMainPage()
{
    var firstPage = new NavigationPage(new MyRootPage())
    {
        Navigation = firstPage.Navigation;
        return firstPage;
    }
}

Now this falls apart when you're using a MasterDetail page because you need your Navigation to wrap your DetailPage, not your MasterDetailPage. Therefore, don't set Navigation in the GetMainPage method, but instead from within the MD Page.

        var master = new MainMenuPage();
        var detail = new NavigationPage(new FirstPage());

        if (App.Navigation == null)
        {
            App.Navigation = detail.Navigation;
        }

        Master = master;
        Detail = detail;

Upvotes: 1

Related Questions