Search code examples
xamarin.formsprism-6

Xamarin.Forms, using the Prism.Forms NavigationService


I am trying to implement a login scenario with Xamarin Forms and Prism.

My application root page is a MasterDetail page (HomeMasterDetailPage); this being used to provide the side menu.

What I am trying to do is to have the HomeMasterDetailPage page shown with Content set to an instance of LoginPage. When the user logs in, the Content should change to an instance of HomeDetailContentPage. It should not be possible to return to the LoginPage using the back button.

I am using the NavigationService from Prism Forms.

My start up is like this...

public partial class App : PrismApplication {

    public App(IPlatformInitializer initializer = null)
        : base(initializer) {
    }

    protected override void OnInitialized() {

        InitializeComponent();

        Uri uri = new Uri($"/{nameof(NavigationPage)}/{nameof(HomeMasterDetailPage)}/{nameof(HomeDetailContentPage)}", UriKind.Absolute);

        var settings = Container.Resolve<SettingsService>();
        if (!settings.DeviceUserID.HasValue)
            uri = new Uri($"/{nameof(NavigationPage)}/{nameof(HomeMasterDetailPage)}/{nameof(LoginPage)}", UriKind.Absolute);

        NavigationService.NavigateAsync(uri);
    }

    protected override void RegisterTypes() {

        Container.RegisterTypeForNavigation<NavigationPage>();

        Container.RegisterTypeForNavigation<LoginPage, LoginPageViewModel>();

        Container.RegisterTypeForNavigation<HomeMasterDetailPage, HomePageViewModel>();
        Container.RegisterTypeForNavigation<HomeMasterContentPage>();
        Container.RegisterTypeForNavigation<HomeDetailContentPage>();

    }

}

This correctly displays the LoginPage within the HomeMasterDetailPage if the user is not already logged on, and the HomeDetailContentPage within the same HomeMasterDetailPage if the user is logged on.

The problem comes when actually logging on; the following is the code within the view model for the LoginPage...

protected async Task ExecuteLoginCommand() {
    Uri uri = new Uri($"/{nameof(NavigationPage)}/{nameof(HomeMasterDetailPage)}/{nameof(HomeDetailContentPage)}", UriKind.Absolute);
    await this.navigationService.NavigateAsync(uri);
}

It is my understanding that by using an absolute Uri this should reset the navigation so that the new page arrangement (/NavigationPage/HomeMasterDetailPage/HomeDetailContentPage) is at the top and bottom of the stack, allowing me to then move forward from there. Instead, I am getting an unhandled exception (on Android 7.0).

What am I doing wrong?

Note: All of the examples that I see have the MasterDetailPage at the root with Content being set to NavigationPage/ContentPage; when I try this I do not get exceptions, however the side menu operates differently within the MasterDetailPage - when you tap on the "hamburger" the side menu slides in over everything (including the action/title bar) and the only way to clear it is to tap outside of the side menu that slides in whereas when I show the MasterDetailPage within a NavigationPage the side menu slides in below the action/title bar and the hamburger changes to an arrow that you can click to hide the side menu again.


Solution

  • I can't say I've tested this exact scenario. However, if your LoginPage is the Detail of a MasterDetailPage, you may not want to perform the navigation from the LoginPage. You could, instead create a SuccessfulLoginEvent, which you would then publish from the LoginPage using the IEventAggregator, and you would subscribe to on your MasterDetailPage's ViewModel. You could then perform the navigation as _navigationService.NavigateAsync("NavigationPage/ViewA") and it should reset the Detail such that the back button doesn't bring you to the LoginPage.

    Alternatively, you can simply perform an absolute Navigation which has the effect of: Application.Current.MainPage = new MyPage().

    To do to this, you could do the navigation from anywhere as: _navigationService.NavigateAsync("/MyMasterDetailPage/NavigationPage/ViewA")