Showing different toolbar buttons on each page with Xamarin Forms - xamarin

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.

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();

How to create a custom WebView Control Xamarin forms

I am trying to create a custom control for webview, i am trying to get a checkbox inside a webview Reason :- we have a bunch of text to be displayed and unless the user reaches the end of the scroll he cannot move to the next page and at the end of the scroll there is a checkbox where user has to check the the checkbox and then he can process. here i have tried putting the checkbox and webview inside the stacklayout but the issue is webview have its own scroll bar and and stacklayout scroll bar does not work when a user try to scroll as the webview scroller scrolls out also when i try to close the Webview Page with back button the webview gets close and not the page
i am not sure what approach should i apply here.
i am getting my html data from my webapi.
anyone with some solution would be appreciable
here is my custom renderer which i have wrote but the piece missing here is how can i add another xamarin control inside this
public class CustomPdfViewRenderer : WebViewRenderer
{
public CustomPdfViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Settings.BuiltInZoomControls = true;
Control.Settings.DisplayZoomControls = false;
Control.Settings.LoadWithOverviewMode = true;
Control.Settings.UseWideViewPort = true;
}
}
}
You can try the following approach:
Add checkbox to HTML
When the user check checkbox call some JavaScript function
When JavaScript function is called, call C# function (Xamarin) which will enable the user to process to the next page (or some other Xamarin side stuff)
Here is how you can call C# function from JavaScript :HybridWebView

CarouselView not showing on Android if in a modal page

I am trying to handle a login/registration form inside a CarouselView (https://github.com/alexrainman/CarouselView). In my MainPage, I have this:
protected override async void OnAppearing()
{
base.OnAppearing();
// Is user logged in?
if (!(bool)Application.Current.Properties["IsLoggedIn"]) // He is not logged in
{
await this.Navigation.PushModalAsync(new LoginRegistrationPage());
}
}
LoginRegistrationPage contains a CarouselView and its first page is the registration form, while the second page is the login form.
iOS is fine, but on Android I get a blank page. The CarouselView is visible everywhere except that in a modal page. (I have tried to set LoginRegistrationPage as MainPage and to put it inside a simple Navigation rather than a modal one, both work).
It was a bug of CarouselView.
This solved: https://github.com/alexrainman/CarouselView/pull/329

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 :)

Xamarin.Forms removing a page

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.

Resources