Xamarin.Forms removing a page - xamarin

When my app first starts up I have it display a login page. In the login button if they are able to login I want to then remove the login page and navigate to a tabbed page. In this tabbed page I'll have a settings page that would allow me to get back to the login page if needed. Right now I have the following but it doesn't work. The HomePage is shown but the back arrow to the login page shows up and I don't want that.
public class LoginPage: ContentPage
{
public LoginPage() { // create controls here }
public btnLogin_Clicked(object sender, EventArgs e){
Navigation.PopAsync(); // remove this page (doesn't work)
Navigation.PushAsync(new HomePage());
}
}
public class App : Application
{
public App()
{
MainPage = new NavigationPage(new LoginPage());
}
}

Xamarin.Forms 1.3 added the capability to add and remove pages resetting the root of the navigation stack as you suggest. Your code indicates that you are using at least version 1.3. However, calling PopAsync() right off the bat is not the method you want to use as it will not pop off a page if it is the only page in the stack. Instead use the INavigation interface's InsertPageBefore(newPage, pageToPutBefore) method first and then pop the login page off the end of the stack.
You can try code similar to this:
public async void btnLogin_Clicked(object sender, EventArgs e)
{
// Do some login logic and if successful ...
Navigation.InsertPageBefore(new HomePage(), this);
await Navigation.PopAsync().ConfigureAwait(false);
}
There are several new methods in Xamarin.Forms 1.3 that substantially improve the navigation capabilities. Another possible solution to the above problem would be to first add the HomePage to the end of the stack and then use the new RemovePage method to remove the login page from the start of the stack leaving the HomePage as the only page left. One thing you want to be careful of, if you are adding the new page using an asynchronous method like PushAsync you will need to await to call to ensure the new page is finished being added to the stack before removing the old page.

Yet another solution: Change MainPage property
in App.cs Constructor:
public App()
{
MainPage = new LoginPage();
}
in your Login method:
Device.BeginInvokeOnMainThread(() =>
{
Application.Current.MainPage = new NavigationPage(new HomePage());
});

For your second point , the back arrow to the login page shows up and you don't want that >>
Use this NavigationPage.SetHasBackButton(YourPage, false);
This will remove the Back Button from your navigation bar
As an example for your code above,
HomePage myHomePage = new HomePage();
NavigationPage.SetHasNavigationBar(myHomePage , false);
Navigation.PushAsync(myHomePage);
You can explore more methods of NavigationPage - such as SetHasNavigationBar and many more, they are really good.
Please let me know if this helps.

Related

How can I set up my application main page (shell) from the OnAppearing of a login page with Xamarin Forms?

I have an application that I added a launch page to in the iOS and Android code. However when the app starts there is still quite a long delay while it fetches data. At this time there's a blank screen where I assume the app is still setting up the constructor.
I am trying to have an in-between page where that appears that loads the data. Not sure if this is the best way to do this but so far it's all that I have.
Here's the code that I have so far:
public App()
{
InitializeComponent;
MainPage = new NavigationPage(new Test.LoginPage())
{
};
}
My Test.LoginPage is a simple empty Xaml page with this C# back end:
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
await LongRunningTask();
App.MainPage = new AppShell(); // I want to start a shell app
}
}
public partial class AppShell : Shell
{
public AppShell()
{
Routing.RegisterRoute("HomeTab/QHPage", typeof(QHPage));
// etc
But the code has issues in that first of all I am not sure I am doing it correctly and secondly it says an object reference is required for App.MainPage.
Can anyone point me in the right direction and suggest how I could display this intermediate page and then display the real app pages?
Note that at some point I would also like to have a button on the login page that when clicked takes me to the app. But at this time I just want to get even the most simple version working so I am looking for some advice with that.
The easy way is to set the AppShell as MainPage. The code below works for me.
Application.Current.MainPage = new AppShell();

What is the solution for pushasync is not supported globally in xamarin forms?

I am working on xamarin forms, Where I am getting an error like pushasync is not supported globally on xamarin forms. Please find my code
public App()
{
InitializeComponent();
MainPage = new MSLogin();
}
I am setting My login page as the main page. Once the user logged in successfully based on the user role I need to navigate to different Dashboards. I am using MasterPage as my template and how I am navigating user based on the role is
Application.Current.MainPage = new MainPage();
if(role=="a")
{
Navigation.PushAsync(new Dashboard1());
}
else
{
Navigation.PushAsync(new Dashboard2())
}
Already so many people got this error and solutions are also available but not working in my scenario. How to solve this?
You need to include your Page inside NavigationPage to Support navigation.
So your code change would be :-
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MSLogin());
}
Kindly change this and it should work.

Why doesn't Navigation.PopAsync() trigger the OnAppearing method of the underlying page?

My Xamarin Forms Application has a MainPage set to
new NavigationPage(new CarsMasterDetailPage())
where CarsMasterDetailPage : MasterDetailPage.
In CarsMasterDetailPage constructor, I set the Master property to new CarsMasterPage() and the Detail property to new CarsDetailPage(). Both CarsMasterPage and CarsDetailPage extend ContentPage.
The master page contains a list of cars and a button which has an event handler that does:
await Navigation.PushAsync(new AddCar());
The AddCar page has a button with an event handler that does:
await Navigation.PopAsync();
When I first run the app, the OnAppearing method of the master page is called. The first time the navigation pops back to the master page, OnAppearing is called again. Subsequent navigation pushes and pops don't, though.
I can get around it by adding a delegate on the master page that the add car page calls when it's done, but that feels hacky since there are page events to handle this. Does anyone know why it's not working?
In my case, I did it like this and everything works fine:
My MainPage:
MainPage = new MasterDetailPage {
Master = new MasterDetailPage1Master(),
Detail = new MyNavigationPage(new MainPageLinearList(appType))
};
So the key point was to use NavigationPage in a DetailPage. After that everything works fine.
Subscribe to the NavigationPage.Popped event:
navigationPage.Popped += OnPopped;
...
void OnPopped(object sender, NavigationEventArgs e)
{
var currentPage = (sender as NavigationPage).CurrentPage;
var removedPage = e.Page;
...
}

Strange NavigationBar behaviour in Xamarin Forms

I have a Login page that is the starting point of my application. This page has navigation bar hidden with the following code:
NavigationPage.SetHasNavigationBar(this, false);
From Login page I start my Dashboard page using the following code:
Navigation.PushAsync(new Dashboard());
I do this on two seperate places inside my Login page. First place is inside constructor of Login page where I check for active user session and the second place is inside method that handles button click for login.
So I call Navigation.PushAsync() like this:
1st way:
public Login()
{
NavigationPage.SetHasNavigationBar(this, false);
InitializeComponent();
var loginSession = App.DataService.CheckUserSession();
if(loginSession != null)
{
Navigation.PushAsync(new Dashboard());
}
}
2nd way:
public void OnLogin(object o, EventArgs e)
{
Navigation.PushAsync(new Dashboard());
}
And this is where things start to go strange. If I open my Dasboard page with button click (using 2nd example) the Dasboard page gets loaded normally and has it's own NavigationBar. However everytime that I open Dasboard page when user session is found (using 1st way), my Dasboard page will have missing NavigationBar. To make things even more strange, if I navigate from Dasboard page to another page (lets say Settings) and then return back to Dasboard, navigation bar will then work normally on Dasboard page. I have tried removing the code that hides navigation bar on Login page NavigationPage.SetHasNavigationBar(this, false);
and what this does is that Login page will have it's navigation bar shown, but upon navigation to Dashboard (using 1st way), navigation bar will remain the same as on Login page.
I was maybe wondering if the problem lies within the fact that all my pages (Login, Dashboard and Settings) are child's of ContentPage and would have to instead inherit from NavigationPage?
I am really confused about what's happening with my NavigationBar at this moment.
It's because the Dashboard page is being pushed from inside the Login constructor. Instead, override OnAppearing() and do it there:
protected override async void OnAppearing()
{
base.OnAppearing();
await Navigation.PushAsync(new Dashboard());
}
Also, your children pages should inherit from ContentPage as nesting NavigationPages could lead to other issues :)

Showing different toolbar buttons on each page with Xamarin Forms

I have 2 pages in my Xamarin Forms app. My first page has 4 icons in the toolbar. My second page is a login page and has a tick and a cross in the toolbar.
I can't get the login page to show any icons unless I make it a navigation page. I also have to clear ToolBarItems on the first page before calling PushAsync() otherwise it complains there are too many toolbar items.
If I call PopAsync() on the login page it does not return to the first page. I'm guessing this is due to their being 2 navigation pages. I also tried PopToRootAsync().The back button works however.
My question is - how do I show different toolbar icons on 2 different pages in a way that allows navigation to work?
I'm testing this on Windows Phone 8.0
Here is the code calling the login page:
private async void ShowLoginPage()
{
ToolbarItems.Clear();
var page = new NavigationPage(new LoginPage());
await Navigation.PushAsync(page);
}
and here is the code to return to the first page:
private void Cancel()
{
Navigation.PopToRootAsync();
}
I'm running Xamarin.Forms v1.2.2.6243
One thing you could try is to keep your Login Page inside of a NavigationPage, and then instead of running PopAsync() within the Login Page after they have logged in successfully, simply replace the MainPage with your old Navigation page:
In your App class:
public NavigationPage AppNavPage = new NavigationPage(new FirstPage());
public App() {
MainPage = AppNavPage;
}
In your FirstPage:
private async void ShowLoginPage() {
ToolbarItems.Clear();
var page = new NavigationPage(new LoginPage());
await Navigation.PushAsync(page);
}
In Login Page:
private async void OnCreateClicked(object sender, EventArgs e) {
bool loginInfoIsGood = CheckLoginInfo(); //Check their login info
if(loginInfoIsGood) {
Application.Current.MainPage = App.AppNavPage;
}
}
Otherwise, I have also done a custom renderer for the NavigationRenderer on iOS to insert toolbar items onto the right side of the Navigation Bar and have overridden some Menu related stuff on Android to change the icon text/colors.
One option that you have, and one that I implemented in my own app, is a custom renderer that removes the navigation header from the app and then you could build your own custom header. With this approach, you do lose some of the native feel of the app, and you have to implement much of the transitional functionality your self. However, it gives you alot more control over the look.
CustomRenderer that removes the navigationBar:
//add using statements
// add all view here that need this custom header, might be able to build a
//base page that others inherit from, so that this will work on all pages.
[assembly: ExportRenderer(typeof(yourView), typeof(HeaderRenderer))]
class HeaderRenderer : PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.NavigationController.SetNavigationBarHidden(true, true);
}
}
After this you can build a header view that can be placed on the top of every page (I am using xaml) so I don't know if it is relevant in you application.
Edit: You might need to change this renderer for differnt page types.

Resources