Xamarin Forms MVVM Clear navigation stack from INavigationService

The project I am working on contains the following structure:

When the application is launched, the user sees a welcome page. At this point, the user has two options. They can either sign in or register. If registered == true; then go to the main details page. Or on registration, if register == success, go to the login page and follow the same process and finish the wizard page.

                -> Login Page                  ||
Welcome Page >> ==================             || => MasterDetailPage
                -> Register Page -> Login page ||

      

I am using MVVM Light to handle my navigation stack through the InavigationService since my interface and business logic are separated using MVVM. Everything works very well, except that I need to reset the navigation stack so that the user cannot access any page before the "MasterDetailPage" shown above. Right now, users can go back to registering or registering, or whatever page they've been on before, by using the Back button on Android, or by scrolling to the left on iOS. Also, there is a back navigation button on the navbar anyway.example navigation bar

My App.cs looks something like this.

public App()
{
    var nav = RegisterNavigationService();
    SimpleIoc.Default.Register<INavigationService>(() => nav);

    InitializeComponent();

    var initialPage = new NavigationPage(new WelcomePage());
    nav.Initialize(initialPage);
    MainPage = initialPage;
}

private NavigationService RegisterNavigationService()
{
    var nav = new NavigationService();
    nav.Configure(Locator.LoginForm, typeof(LoginForm));
    nav.Configure(Locator.RegisterSuccessPage, typeof(RegisterSuccessPage));
    nav.Configure(Locator.RegistrationForm, typeof(RegistrationForm));
    nav.Configure(Locator.WelcomePage, typeof(WelcomePage));
    nav.Configure(Locator.MasterMainPage, typeof(MasterMainPage));
    return nav;
 }

      

In my viewmodes, I handle navigation commands like this:

public class LoginFormViewModel : BaseViewModel
{
    private readonly INavigationService _navigationService;
    public Command NavigateToMainPage { get; }

    public LoginFormViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService ?? throw new ArgumentNullException("navigationService");

        NavigateToMainPage = new Command(() => NavigateToMainApp());
    }

    private void NavigateToMainApp()
    {
        _navigationService.NavigateTo(Locator.MasterMainPage);
    }
}

      

Finally, my NavigationService.cs looks like this ... I barely touched this part of the code ... The only one I tried was the ClearNavigationStack method, but it crashed.

public class NavigationService : INavigationService, INavigationServiceExtensions
{
    private Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>();
    private NavigationPage _navigation;

    public string CurrentPageKey
    {
        get
        {
            lock (_pagesByKey)
            {
                if (_navigation.CurrentPage == null)
                {
                    return null;
                }

                var pageType = _navigation.CurrentPage.GetType();

                return _pagesByKey.ContainsValue(pageType)
                    ? _pagesByKey.First(p => p.Value == pageType).Key
                    : null;
            }
        }
    }

    public void GoBack()
    {
        _navigation.PopAsync();
    }

    public void NavigateTo(string pageKey)
    {
        NavigateTo(pageKey, null);
    }

    public void NavigateTo(string pageKey, object parameter)
    {
        lock (_pagesByKey)
        {
            if (_pagesByKey.ContainsKey(pageKey))
            {
                ConstructorInfo constructor;
                object[] parameters;
                var type = _pagesByKey[pageKey];

                if (parameter == null)
                {
                    constructor = type.GetTypeInfo()
                        .DeclaredConstructors
                        .FirstOrDefault(c => !c.GetParameters().Any());

                    parameters = new object[] { };
                }
                else
                {
                    constructor = type.GetTypeInfo()
                        .DeclaredConstructors
                        .FirstOrDefault(
                            c =>
                            {
                                var p = c.GetParameters();
                                return p.Count() == 1
                                       && p[0].ParameterType == parameter.GetType();
                            });

                    parameters = new[] { parameter };
                }

                if (constructor == null)
                {
                    throw new InvalidOperationException("No suitable constructor found for page " + pageKey);
                }

                var page = constructor.Invoke(parameters) as Page;
                _navigation.PushAsync(page);
            }
            else
            {
                throw new ArgumentException(
                    string.Format("No such page: {0}. Did you forget to call NavigationService.Configure?", pageKey), "pageKey");
            }
        }
    }

    public void Configure(string pageKey, Type pageType)
    {
        lock (_pagesByKey)
        {
            if (_pagesByKey.ContainsKey(pageKey))
            {
                _pagesByKey[pageKey] = pageType;
            }
            else
            {
                _pagesByKey.Add(pageKey, pageType);
            }
        }
    }


    public void ClearNavigationStack()
    {
        lock (_pagesByKey)
        {
            foreach (var pageKey in _pagesByKey.Keys)
            {
                _pagesByKey.Remove(pageKey);
            }
        }   
    }

    public void Initialize(NavigationPage navigation)
    {
        _navigation = navigation;
    }
}

      

I took this bit from the following git repo: https://github.com/mallibone/MvvmLightNavigation.XamarinForms

following this tutorial: https://mallibone.com/post/xamarin.forms-navigation-with-mvvm-light

Note. This is PCL.

Any suggestion is greatly appreciated as I've been on this for the past 2 days.

The EDIT: . I have now managed to "hide" the nav stack by setting my MainPage to something like this

App.Current.MainPage = new MasterMainPage();

      

But it looks like a code smell and looks like a terrible hack. Plus I'm not too sure if it "violates" the concepts I'm following ... And I think this navigation stack will never go away as I will be doing other navigation stacks inside the main help pages.

+3


source to share


1 answer


From your photo, I can see that you have a Master / Detaied page inside your navigation page. Hamarin does not recommend doing this. I don't know how you are going to do this in MVVM Light, but in normal forms you have a couple of options to achieve what you want:

  • If you need to return to the login or registration page, you must use

    await Navigation.PushModalAsync(new YourMasterDetailPage());
    
          

Then you can use popmodal to return to them, but in this case the Hardware button will still take you to Login. You can use part of method 2 to clear the stack after you have navigated to the master detail page, but be careful: you cannot remove the page from the stack if it is the start and current page, so you will need to clear the normal navigation stack only after the login page is not displayed. I would not recommend this option as "Modals are often temporary and appear on the screen long enough for the user to complete a task."



http://blog.adamkemp.com/2014/09/navigation-in-xamarinforms_2.html

  1. If you don't need to go back, you can use the following to clear the navigation stack, it will also remove the back button

    await Navigation.PushAsync(new YourMasterPage());
    var pages = Navigation.NavigationStack.ToList();
    foreach (var page in pages)
    {
        if (page.GetType() != typeof(YourMasterPage))
            Navigation.RemovePage(page);
    }
    
          

+1


source







All Articles