Forum Xamarin.Forms

Navigation in ViewModel

Hello,
I'm new to MVVM/XAML and want to implement navigation in my App.

In the View I have:

        public LoginPageX()
        {
            InitializeComponent();
            BindingContext = new LoginViewModel(Navigation);
        }

And in my ViewModel:

        public class LoginViewModel
        {
            public Command LoginCommand { get; set; }
            INavigation navigation;

            public LoginViewModel(INavigation loginPageNav)
            {
                navigation = loginPageNav;
                LoginCommand = new Command(async () => await OfflineButton());
            }


            public async Task OfflineButton()
            {
                navigation.InsertPageBefore(new MainPageX(), LoginPageX);
                await navigation.PopAsync();
            }
        }

I'm a little confused about how to navigation.InsertPageBefore(new MainPageX(), what page?);

Best Answers

Answers

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭

    Depends on what you are trying to do.

    Personally, I don't think that is how you want to navigate here.

    When doing a login, you want to swap the MainPage of your App class from the LoginPage to the MainPageX.

    Basically resetting the root of your application to the new main page.

    Now if MainPageX is not a MasterDetailPage, you probably want to wrap it in a NavigationPage to allow you to just call Navigation.PushAsync for new pages being added.

    Navigating from the View Model is also an opinionated matter, more advice on how to structure it would depend on if you want ViewModel to ViewModel navigation or Page-Page navigation.

  • LucasSossaiLucasSossai Member ✭✭✭

    @AdamMeaney said:
    Depends on what you are trying to do.

    Personally, I don't think that is how you want to navigate here.

    When doing a login, you want to swap the MainPage of your App class from the LoginPage to the MainPageX.

    Basically resetting the root of your application to the new main page.

    Now if MainPageX is not a MasterDetailPage, you probably want to wrap it in a NavigationPage to allow you to just call Navigation.PushAsync for new pages being added.

    Navigating from the View Model is also an opinionated matter, more advice on how to structure it would depend on if you want ViewModel to ViewModel navigation or Page-Page navigation.

    I want to implement hierarchical navigation in my App, and probably ViewModel to ViewModel instead of Page to Page navigation.

  • LucasSossaiLucasSossai Member ✭✭✭
    edited December 2018

    @JohnHardman said:

    @LucasSossai said:
    I want to implement hierarchical navigation in my App, and probably ViewModel to ViewModel instead of Page to Page navigation.

    See https://docs.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/navigation
    (the whole book is worth reading, but that link goes specifically to the navigation chapter)

    Thank you, It was really good reading, I just started implementing MVVM now and I'm migrating the project I'm working now from declarative code to XAML/MVVM and it really helped me.

    I'm tring to implement a Logout Navigation method now, I made the INavigationService and NavigationService but I dont know how to find the Current Page that I'm to Insert a page before ( a Login Page) and Pop it, this is my method in NavigationService:

            public async Task Logout()
            {
                var mainPage = Application.Current.MainPage;
    
                if(mainPage != null)
                {
                    for(int i = 0; i < mainPage.Navigation.NavigationStack.Count - 1; i++)
                    {
                        var page = mainPage.Navigation.NavigationStack[i];
                        mainPage.Navigation.RemovePage(page);
                    }
                }
                await Application.Current.MainPage.Navigation.InsertPageBefore(new LoginPageX(), **whatpage?**);
                await Application.Current.MainPage.Navigation.PopAsync();
            }~~~~
    

    Can you help me please?

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭
    public void Logout()
    {
        Application.Current.MainPage = new LoginPageX();
    }
    

    This is the best way. No hierarchy on Login Page, just a new page.

    When they log back in, set the main page to the root of your navigation.

  • NMackayNMackay GBInsider, University mod

    I don't want to derail this discussion, but just some samples on how to do injected navigation in MVVM Light and Prismlib

    MVVM Light
    https://github.com/mackayn/MVVM-Light-Sample/blob/master/XamFormsTestApp.Data/ViewModel/StartViewModel.cs

    Full source: https://github.com/mackayn/MVVM-Light-Sample

    Prism
    https://github.com/mackayn/CrudSample/blob/master/CrudSample/ViewModels/MainPageViewModel.cs

    Full source: https://github.com/mackayn/CrudSample

    Prism is the nicest by far (other frameworks are available) I've found for navigation

       await _navigationService.NavigateAsync("Editor", 
                 new NavigationParameters { { NavigationParams.IncidentId, SelectedIncident } },true,false);
    
  • LucasSossaiLucasSossai Member ✭✭✭

    @AdamMeaney said:
    public void Logout()
    {
    Application.Current.MainPage = new LoginPageX();
    }

    This is the best way. No hierarchy on Login Page, just a new page.

    When they log back in, set the main page to the root of your navigation.

    This is a great solution, I just needed to set the LoginPageX to be a NavigationPage like > @JohnHardman said:

    @LucasSossai

    I'm not entirely clear what your scenario is. I am guessing that you have 1 or more pages on the NavigationStack and on executing Logout want to end up with LoginPageX as the only page on the NavigationStack.

    If that is the case, you could do something along the lines of (or put another way, I haven't tested this...):

    Application.Current.MainPage.Navigation.InsertPageBefore(new LoginPageX(), Application.Current.MainPage.Navigation.NavigationStack[0]);
    await Application.Current.MainPage.Navigation.PopToRootAsync();
    Application.Current.MainPage = new NavigationPage(Application.Current.MainPage.Navigation.NavigationStack[0]);
    

    It might be that you could simplify that to:

    Application.Current.MainPage = new NavigationPage(new LoginPageX());
    

    But I've not tried it, so don't know whether just changing MainPage does everything required.

  • LucasSossaiLucasSossai Member ✭✭✭

    @NMackay said:
    I don't want to derail this discussion, but just some samples on how to do injected navigation in MVVM Light and Prismlib

    MVVM Light
    https://github.com/mackayn/MVVM-Light-Sample/blob/master/XamFormsTestApp.Data/ViewModel/StartViewModel.cs

    Full source: https://github.com/mackayn/MVVM-Light-Sample

    Prism
    https://github.com/mackayn/CrudSample/blob/master/CrudSample/ViewModels/MainPageViewModel.cs

    Full source: https://github.com/mackayn/CrudSample

    Prism is the nicest by far (other frameworks are available) I've found for navigation

       await _navigationService.NavigateAsync("Editor", 
                 new NavigationParameters { { NavigationParams.IncidentId, SelectedIncident } },true,false);
    

    Thank you for the suggestion @NMackay , do you recommend to always use frameworks?

  • NMackayNMackay GBInsider, University mod

    @LucasSossai said:

    @NMackay said:
    I don't want to derail this discussion, but just some samples on how to do injected navigation in MVVM Light and Prismlib

    MVVM Light
    https://github.com/mackayn/MVVM-Light-Sample/blob/master/XamFormsTestApp.Data/ViewModel/StartViewModel.cs

    Full source: https://github.com/mackayn/MVVM-Light-Sample

    Prism
    https://github.com/mackayn/CrudSample/blob/master/CrudSample/ViewModels/MainPageViewModel.cs

    Full source: https://github.com/mackayn/CrudSample

    Prism is the nicest by far (other frameworks are available) I've found for navigation

       await _navigationService.NavigateAsync("Editor", 
                 new NavigationParameters { { NavigationParams.IncidentId, SelectedIncident } },true,false);
    

    Thank you for the suggestion @NMackay , do you recommend to always use frameworks?

    For larger apps yes, it will save you time but learning the nav scheme and trying your own lets you make a better judgement of what frameworks do and what will be best for you. Prism is worth a look once your comfortable with the vanilla framework.

  • adrianex03adrianex03 Member ✭✭

    If you are using ViewModels you can implement this easily with ICommand.

    namespace YourApp.ViewModels
    {
        public class CurrentPageViewModel
        {
            public ICommand BackToPage  {get; private set; }
    
            public CurrentPageViewModel()
            {
                BackToPage = new Command(async () => {
                  await  Application.Current.MainPage.Navigation.PushModalAsync(new MainPage());
                });
            }
        }
    }
    

    And in the ViewModel of the page that you want to go, you need to implement the PopAsync as follows.

    namespace YourApp.ViewModels
    {
        public class MainPageViewModel 
        {
            public ICommand BackToMain { get; private set; }
    
            public MainPageViewModel()
            {
                BackToMain = new Command(async () => {
                    await Application.Current.MainPage.Navigation.PopAsync();
                });
            }
        }
    }
    

    Also remember to use the Bindings at your Views CodeBehind on both the current page and that you want to go the like this.

    namespace RealmApp1.Views
    {
        public partial class MainPage : ContentPage
        {
            public MainPage()
            {
                InitializeComponent();
                BindingContext = new MainPageViewModel();
            }      
        }
    }
    

    Hope it works for you!
    Have A Nice Code!!!

Sign In or Register to comment.