MasterDetailPage breaks when rendered as NavigationPage - c#

I would like to create an application with a navigation drawer. So each page should be accessible via drawer "link" item.
To protect those pages the application should only be accessible on a valid session otherwise the login page should be rendered.
I'm trying to explain what I've done so far for reproduction purposes...
After creating a new Xamarin forms project I created a MainPage holding the drawer and the currently rendered page
public class MainPage : MasterDetailPage
{
public MainPage()
{
Master = new MasterPage();
Detail = new NavigationPage(new DummyPage()); // Set initial page
}
}
So the MasterPage (drawer) is empty for now
internal class MasterPage : ContentPage
{
public MasterPage()
{
Title = "Master";
Content = new StackLayout { Children = { } };
}
}
Same for the rendered DummyPage
class DummyPage : ContentPage
{
public DummyPage()
{
Title = "Dummy";
Content = new StackLayout { Children = { new Label { Text = "Dummy Page" } } };
}
}
So without a LoginPage my App file would look like
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
}
The rendered application then looks fine
When I try to render a LoginPage before accessing the main application part I would do something like
public partial class App : Application
{
public App()
{
InitializeComponent();
Page initialPage;
if (false) // check if logged in
{
initialPage = new LoginPage();
}
else
{
initialPage = new MainPage();
}
MainPage = new NavigationPage(initialPage);
}
}
Unfortunately the rendered MainPage then looks like
How can I fix that? What is missing or wrong?
Update
I found out that I can set the current displayed page globally. So for the start I do
public App()
{
InitializeComponent();
Page initialPage;
if (isLoggedIn)
{
initialPage = new LoginPage();
}
else
{
initialPage = new MainPage();
}
MainPage = initialPage;
}
and when clicking the "Sign In" button on the LoginPage I don't execute
SignInCommand = new Command(() =>
{
await Application.Current.MainPage.Navigation.PushAsync(new MainPage());
});
This works for me
SignInCommand = new Command(() =>
{
Application.Current.MainPage = new MainPage();
});

You should try this
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage(false);
}
}
public class MainPage : MasterDetailPage
{
public MainPage(bool value)
{
Master = new MasterPage();
Detail = value ? new NavigationPage(new LoginPage()) : new NavigationPage(new DummyPage());
}
}
I think navigation doesn't add the main container.

It seems that you put the MasterDetailPage in a NavigationPage . Actually , it is nappropriate to do it . You could improve the code like following
public partial class App : Application
{
public App()
{
InitializeComponent();
if (false) // check if logged in
{
MainPage = new NavigationPage(initialPage);
}
else
{
MainPage = new MainPage();
}
}
}

Related

How can I set the value of a property on a class that I pass into another class?

I have an application that looks like this:
public partial class MainPage : TabbedPage
{
public bool DarkTheme
{
get { return _darkTheme; }
set { _darkTheme = value; OnPropertyChanged(); }
}
bool _darkTheme;
public MainPage()
{
InitializeComponent();
App.settingsTabPage = new SettingsTabPage(this);
And on the SettingsTabPage I have this:
public partial class SettingsTabPage : ContentPage
{
public TabbedPage mainPage;
public SettingsTabPage(TabbedPage mp)
{
InitializeComponent();
mainPage = mp;
But when I want to make a change to DarkTheme like this then it cannot find the DarkTheme:
mainPage.DarkTheme << can't find DarkTheme
Can someone give me some advice on what I am doing wrong?
Inside SettingsTabPage class, you must either cast mainPage to MainPage class, or declare type of mainPage variable as MainPage class.
either
(mainPage as MainPage).DarkTheme = ...
or
public partial class SettingsTabPage : ContentPage
{
public MainPage mainPage;
public SettingsTabPage(MainPage mp)
{
InitializeComponent();
mainPage = mp;

PushAsync is not supported globally on Android, please use a NavigationPage

I'm doing a Master Detail Page as a menu and I'm getting this error:
PushAsync is not supported globally on Android, please use a NavigationPage.
When I try to instantiate another page. I believe my code already creates a navigation page when instantiate the page, but I'm not sure. Can someone help me?
App.xaml.cs:
public partial class App : Application
{
static public MasterDetailPage MasterDetail { get; set; }
public async static Task NavigateMasterDetail(Page page)
{
App.MasterDetail.IsPresented = false;
await App.MasterDetail.Navigation.PushAsync(page);
}
public App ()
{
InitializeComponent();
MainPage = new selectPage();
}
Page that carry the menu, selectPage:
public partial class selectPage : MasterDetailPage
{
public selectPage()
{
InitializeComponent();
this.Master = new Master();
this.Detail = new NavigationPage(new Detail());
App.MasterDetail = this;
}
}
Master.xaml.cs:
public Master ()
{
InitializeComponent ();
toDivPage.Clicked += async (sender, e) =>
{
await App.NavigateMasterDetail(new MainPage());
};
toBiqPage.Clicked += async (sender, e) =>
{
await App.NavigateMasterDetail(new MainPage());
};
}
Detail.xaml.cs is empty.
I believe you have to use NavigationPage in your App.xaml.cs.
In your App.xaml.cs make sure your constructor or App method looks like this
public App ()
{
InitializeComponent();
MainPage = new NavigationPage(new selectPage());
}
I hope this helps

Xamarin Forms Master Detail Keep Master on the left

I am trying to keep the Master page (menu) to the left of My MasterDetailPage at all times, I do not want the navigation to take the user away from the MasterDetailPage and make the user press a back button to get back.
I am looking a traditional burger bar menu to the left that simply changes the Detail page and keeps the master in place.
What I have now:
public partial class App : Application
{
public static NavigationPage NavPage { get; private set; }
private static RootPage RootPage;
public static bool MenuIsPresented
{
get
{
return RootPage.IsPresented;
}
set
{
RootPage.IsPresented = value;
}
}
public App()
{
InitializeComponent();
MenuPage menuPage = new MenuPage();
NavPage = new NavigationPage(new FirstPage());
RootPage = new RootPage();
RootPage.Master = menuPage;
RootPage.Detail = NavPage;
MainPage = RootPage;
}
...
}
And my ViewModel Command
async void GoSecondPageAsync(object obj)
{
await App.NavPage.PushAsync(new SecondPage());
App.MenuIsPresented = false;
}
But this just creates a new page on top of the stack with a back button back to the MasterDetailPage, while all I want is to stay within the MasterDetailPage.
You can push the new page and just remove the first one from the NavigationStack For example:
async void GoSecondPageAsync(object obj)
{
await App.NavPage.PushAsync(new SecondPage());
var removePage = App.NavPage.Navigation.NavigationStack[0];
App.NavPage.Navigation.RemovePage(removePage);
App.MenuIsPresented = false;
}
Although, if you just always want to replace the page do you really even need the NavigationPage and can't you just set the RootPage.Detail=new SecondPage();
You just need to update the Detail property to point to the new page. Like this:
async void GoSecondPageAsync(object obj)
{
RootPage.Detail = new NavigationPage(new SecondPage());
App.MenuIsPresented = false;
}

OnAppearing in children of TabbedPage is triggered only once

I have a TabbedPage in Xamarin.Forms:
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var playPage = new NavigationPage(new PlayPage())
{
Title = "Play",
Icon = "play1.png"
};
var settingsPage = new NavigationPage(new SettingsPage())
{
Title = "Settings",
Icon = "settings.png"
};
var aboutPage = new NavigationPage(new AboutPage())
{
Title = "About",
Icon = "about.png"
};
Children.Add(playPage);
Children.Add(settingsPage);
Children.Add(aboutPage);
}
Each of the child pages are a ContentPage that overrides the OnAppearing method. The content of my child pages are not updating properly when I navigate between the tabs and further debugging tells me that the OnAppearing method for child pages are only called once (when the MainPage is first loaded).
Anyone have any idea why the OnAppearing method is only called once? And how can I solve this issue?
More Info
My child pages such SettingsPage contains events that when fired open another ContentPage using Navigation.PushAsync method. I expected the OnAppearing method to also get called when switching from tab pages and the navigation pages within those tab pages. (Hope this makes sense)
It only triggers once since the View is not destroyed when you navigate between tabs.
You can use the CurrentPageChanged event to figure our that a page has changed and signal the view it changes to and do whatever update of the view you need.
this.CurrentPageChanged += PageChanged;
void PageChanged(object sender, EventArgs args)
{
var currentPage = CurrentPage as MyTabPage;
currentPage?.UpdateView();
}
Then in your page:
public void UpdateView()
{
// do whatever you need here to update the page
}
Do not embedded your ContentPages within a "single" level NavigationPage.
The following works fine in terms of the OnAppearing event firing when switching between tabs:
public class PlayPage : ContentPage
{
protected override void OnAppearing()
{
System.Diagnostics.Debug.WriteLine("PlayPage");
base.OnAppearing();
}
}
public class AboutPage : ContentPage
{
protected override void OnAppearing()
{
System.Diagnostics.Debug.WriteLine("AboutPage");
base.OnAppearing();
}
}
public class SettingsPage : ContentPage
{
protected override void OnAppearing()
{
System.Diagnostics.Debug.WriteLine("SettingPage");
base.OnAppearing();
}
}
public partial class MainPage : TabbedPage
{
public MainPage()
{
var playPage = new PlayPage() { Title = "Play" };
var settingsPage = new SettingsPage() { Title = "Settings" };
var aboutPage = new AboutPage() { Title = "About" };
Children.Add(playPage);
Children.Add(settingsPage);
Children.Add(aboutPage);
}
}
public class App : Application
{
public App()
{
this.MainPage = new MainPage();
}
}

Xamarin forms PopModalAsync doesn't trigger

I'm building a Xamarin.Forms application with a facebook login using Xamarin.Auth.
Entry point (app.cs):
public App()
{
MainPage = new WelcomePage();
}
WelcomePage:
using System;
using Xamarin.Forms;
namespace Project
{
public class WelcomePage : ContentPage
{
public WelcomePage()
{
var login = new Button { Text = "Login" };
login.Clicked += (sender, e) =>
{
Navigation.PushModalAsync(new LoginPage());
};
Content = new StackLayout
{
BackgroundColor = Color.FromHex("F0C640"),
Padding = new Thickness(10, 40, 10, 10),
Children = {
new Label { Text = "Please login" },
login
}
};
}
}
}
LoginPage:
using Xamarin.Forms;
namespace Project
{
public class LoginPage : ContentPage
{
}
}
This is empty as I use Xamarin.Auth to authenticate with facebook so I created 2 different renderers on iOS and Android to display the facebook login page. After the login success I call an action which is located in my app.cs file:
public Action SuccessfulLoginAction
{
get
{
return new Action(() => goToMainPage());
}
}
public void goToMainPage()
{
MainPage.Navigation.PopModalAsync();
//MainPage.Navigation.PopToRootAsync();
MainPage.Navigation.PushAsync(new DetailPage());
}
At the end of the authentication the page is white but I believe it's a white page from Facebook after the authentication is done.
The SuccessfulLoginAction is well called so I go through the goToMainPage() function.
I put some breakpoints and PopModalAsync() doesn't do anything, same for PopToRootAsync() and PushAsync()...
The DetailPage is just a page with a Label. If I try to display it at the beginning of the process (app(){}) it display it well.
Thanks.

Categories