Forum Xamarin.Forms

Master/Detail with Navigation Page has issues...

binkman71binkman71 USMember ✭✭

I have a setup where I first display a login page, then switch to a master-detail view for the "main" part of the application. Here's what's happening.

I begin the application by creating a NavigationPage:

public static Page GetMainPage()
{
    return new NavigationPage( new LoginPage );
}

After the user successfully logs in, I call Navigation.PushAsync( masterDetailPage ); to show the new interface.

1) On iOS this works, but I have to call NavigationPage.SetHasNavigationBar( masterDetailPage, false ); first, or I get the nav bar with a Back button on top of the masterDetailPage header.

2) On Windows Phone, the default Detail page shows, but the header is not there, and no way to bring up the Master/menu view.

3) It crashes on Android.

So, to make sure I was doing this properly, I changed GetMainPage to simply use the Master/Detail page:

public static Page GetMainPage()
{
    return new MyMasterDetailPage();
}

This worked as expected on all three platforms.

I then tried using the Master/Detail as the root of the NavigationPage:

    public static Page GetMainPage()
    {
        return new NavigationPage( new MyMasterDetailPage() );
    }

This works on iOS and Windows Phone. On Android, it crashes. For my final test:

public static Page GetMainPage()
{
        NavigationPage _navPage = new NavigationPage( new MyMasterDetailPage() );
        _navPage.PushAsync( new LoginPage() );
        return _navPage;
}

Then, upon successful login, simply call Navigation.PopAsync(); to show the Master/Detail view.

1) On iOS this works, but the Master/Detail page is shifted down the screen by the height of the navigation bar.

2) On Windows Phone, the Master/Detail header (footer?) is there all the time, even on the LoginPage.

3) On Android, the Master/Detail page is missing the header bar, so no way to get to the Master panel.

This is all incredibly frustrating, to say the least. :)

Posts

  • binkman71binkman71 USMember ✭✭

    @ChaseFlorell‌

    Still crashes on Android when the MainActivity calls SetPage. If I PushModalAsync the login page prior to SetPage (in the GetMainPage method), I can see the login page, but as soon as it tries to PopModalAsync, the Android app crashes yet again. No real call stack, nothing to go by (well, the callstack makes no sense after PopModalAsync followed by UpdateActionBar, followed by garbage address and such).

  • ChaseFlorellChaseFlorell CAInsider, University mod
    edited June 2014

    You don't do it prior to SetPage.

    SetPage will set the MyMasterDetailPage... pretend that you're showing this page to everyone whether they're logged in or not.

    THEN, inside your MyMasterDetailPage, you'll override the OnAppearing() method.

    protected override void OnAppearing()
    {
        // use whatever logic you need to determine if the user is authenticated
        if (!user.IsLoggedIn)
        {
            App.Navigation.PushModalAsync(_authPage);
        }
        base.OnAppearing();
    }
    

    And inside your Android App (because Android has a back button, but iOS doesnt), you override the OnBackPressed () method to prevent the user from pressing back if they're not logged in.

    public override void OnBackPressed()
    {
        // This prevents a user from being able to hit the back button and leave the login page.
        // use whatever authentication check that you need.
        if (user.IsLoggedIn)
        {
            base.OnBackPressed();
        }
    }
    
  • binkman71binkman71 USMember ✭✭

    @ChaseFlorell‌, I understood, did it that way, it crashes.

    Just to make sure it isn't my code, I took the "HanselmanApp" example that got posted recently, written by @JamesMontemagno‌ (https://github.com/jamesmontemagno/Hanselman.Forms), updated it to simply instantiate the Master/Detail inside a NavigationPage and...BOOM. Crashes on Android.

  • ChaseFlorellChaseFlorell CAInsider, University mod

    That's strange, because I'm doing exactly what I described with a MasterDetail page and a Login Modal window.

  • binkman71binkman71 USMember ✭✭

    Oh, and thanks for the help, @ChaseFlorell‌.

  • MarioJuniorMarioJunior USMember ✭✭

    @binkman71‌, I just did not understand one thing: In the final solution, so that [LoginPage] redirects back to the [MyDetailsPage]?

  • MarioJuniorMarioJunior USMember ✭✭

    It doesn't work... I'm getting a Unhandled Exception:

    Unhandled Exception: Android.Util.AndroidRuntimeException: Exception of type 'Android.Util.AndroidRuntimeException' was thrown. at Android.Runtime.JNIEnv.CallNonvirtualVoidMethod (IntPtr jobject, IntPtr jclass, IntPtr jmethod, Android.Runtime.JValue[] parms) [0x00084] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.12-series/41933531/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:895 at Android.Views.ViewGroup.RemoveView (Android.Views.View view) [0x00070] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.12-series/41933531/source/monodroid/src/Mono.Android/platforms/android-15/src/generated/Android.Views.ViewGroup.cs:3096 at Xamarin.Forms.Platform.Android.ViewExtensions.RemoveFromParent (Android.Views.View view) [0x00000] in <filename unknown>:0 at Xamarin.Forms.Platform.Android.Platform.PopModalAsync () [0x00000] in <filename unknown>:0 at Xamarin.Forms.NavigationProxy.OnPopModal () [0x00000] in <filename unknown>:0 at Xamarin.Forms.NavigationProxy.PopModalAsync

  • binkman71binkman71 USMember ✭✭
    edited July 2014

    @MarioJunior‌
    Make sure:

    1) The GetMainPage returns your Master/Detail page.
    2) All other pages get wrapped in NavigationPage. I mentioned that above, but didn't give an example.

    So, in your App.cs file in your PCL:

    public class App
    {
            public static INavigation Navigation { get; private set; }
                public static Page GetMainPage()
            {
                    var p = new MyMasterDetailPage();
                    Navigation = p.Navigation;
                    return p;
            }
    }
    

    Then, your master/detail page needs to override OnAppearing():

            protected override void OnAppearing()
            {
                // when showing this window, if the Login hasn't been done, show
                // the login screen...
                if( !MyUserSettingsData.LoginValid )
                {
                    Navigation.PushModalAsync( new NavigationPage( new LoginPage() ) );
                }
                base.OnAppearing();
            }
    

    Then, your LoginPage when it is no longer needed, does this:

    public void UserLogin()
    {
            // some code here doing the login....
            if( login succeeded )
            {
                MyUserSettingsData.LoginValid = true;
                    App.Navigation.PopModalAsync();
            }
    }
    
  • IgnacioFuentesIgnacioFuentes USMember
    edited July 2014

    So what does your MyMasterDetailPage inherit from? A NavigationPage or a ContentPage?

  • binkman71binkman71 USMember ✭✭
    edited July 2014
    public class MyMasterDetailPage : MasterDetailPage
    {
    }
    

    That's it, straight up MasterDetailPage. However, all Detail pages need the NavigationPage wrapper as well. The Master can be a simple ContentPage.

    public MyMasterDetailPage()
    {
        Master = new ContentPage();
        Detail = new NavigationPage( new MyDetailPage() );
    }
    

    Where MyDetailPage is likewise a ContentPage (or other Page type).

  • MarioJuniorMarioJunior USMember ✭✭

    Thank you so much @binkman71‌!

    I created a new Project, with the code you posted, and it Works!

    I simply added a code to prevent user to close the [LoginPage] without have authenticate, but I do that in the Android specific code (MainActivity.cs):

    public override void OnBackPressed()
    {
        if (MyUserSettingsData.LoginValid)
        {
            base.OnBackPressed();
        }
    }
    
  • BillPullBillPull USMember

    Any way to do this with a Login and Register page. So in my MainActivity I have

    ` protected override void OnCreate(Bundle bundle)
    {
    RaygunClient.Attach("i27YVEgFvRzBI8gZoIeMkg==");
    base.OnCreate(bundle);

            App.Init(typeof(App).Assembly);
            Forms.Init(this, bundle);
            FormsMaps.Init(this, bundle);
    
            // Set our view from the "main" layout resource
            SetPage(BuildView());
        }
    
        static Page BuildView()
        {
            return new NavigationPage(new LoginPage());
        }`
    

    and then on my login page I have two buttons one which authenticates you and another that brings you to the
    registration page.

    protected async Task ExecuteRegisterCommand() { await _navigation.PushAsync(new RegisterPage()); }

    and then on the register and login page after a successful authentication call I attempt to send the user to the
    MasterDetail Page.

    await _navigation.PushAsync(new RootPage());

    I tried to do the above suggestion where I send the user to the MasterDetail page first and if they are note authenticated
    then send them back to the login screen. However the suggestion above only accounts for 1 screen being there allowing
    you to disable the back button.

    I have seen some examples where people suggest using modals but that really isn't a design pattern I see in a lot of apps.

  • CraigDunnCraigDunn USXamarin Team Xamurai

    See if this login demo to see if you prefer that approach.

  • BillPullBillPull USMember

    yes that works perfectly thanks

  • san.0128san.0128 USMember

    HI i have an app where i implemented master detail page which i use as menu. The app provide two ways to navigate to other pages. 1. by menu and 2. by dashboard links

    I have dashboard also from where a user can switch to other page. it works fine if i navigate from menu options. but if i want to navigate from my dashboard page to other page then i have a problem. the menu is not appear in the page which i am navigating. ex.

    My home page display menu as well as dashboard page. so if i navigate using menu, it just works fine. but if click on one of the widget on dashboard to navigate to that page, and i don't have any option to find the detail page at that time.

    so can you guide me how to do this?

  • batmacibatmaci DEMember ✭✭✭✭✭

    @CraigDunn said:
    See if this login demo to see if you prefer that approach.

    I cant figure out how the ILoginManager is initialized or resolved in this sample. I get always null after successfully login

Sign In or Register to comment.