Replacing the Xamarin Header/Navigation bar with a custom view

Intro:

I think I've started countless apps and said to myself, hey you really need to sit down and take the time to explain to yourself how exactly these Xamarin Forms custom header bars are actually working to get an understanding on how to maintain existing ones and to be able to develop better ones in the future. So here goes, my explanation of how to build a Xamarin Forms custom header/navigation bar with back button that'll work on any device past and present.

The code:

First things first. In a typical Xamarin Forms project, you'll have an App.cs in the shared/portable project, this is great but we need a .xaml file to go with it, so right click the project, click 'Add' and then click 'New Item...' When the window opens, choose 'Forms Xaml Page' then name it App.xaml and click 'Add'. This'll add the xaml file to your project, but there's already an issue. Yay! Visual Studio has created another cs file for this page called App.xaml.cs, we don't want this as it'll conflict with our existing file so delete the cs file and we're ready to make some changes to the xaml file. Open the xaml file and delete everything in it, then paste in the following code. I'll explain what it does once you've done it. ;)

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyAppNamespace.App">
  <Application.Resources>
    <!-- Application resource dictionary -->
    <ResourceDictionary>
      <ControlTemplate x:Key="MainPageTemplate">

        <Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" ColumnSpacing="0" RowSpacing="0">
          <Grid.RowDefinitions>
            <RowDefinition Height="110"/>
            <RowDefinition Height="*"/>
          </Grid.RowDefinitions>

          <!-- Begin Header -->
          <StackLayout
            Orientation="Vertical"
            HorizontalOptions="FillAndExpand"
            VerticalOptions="FillAndExpand"
            Spacing="0"
            BackgroundColor="#2B2B2B"
            HeightRequest="100"
            Grid.Column="0" Grid.Row="0">
            <StackLayout.Padding>
              <OnPlatform x:TypeArguments="Thickness"
                            iOS="10, 10, 10, 10"
                            Android="10, 10, 10, 10"
                            WinPhone="10, 10, 10, 10" />
            </StackLayout.Padding>
            <!-- My header content to make my app the prettiest. -->
<Image Source="back128.png" WidthRequest="30" HeightRequest="30">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBackButtonPressed" />
</Image.GestureRecognizers>
</Image> </StackLayout> <!-- End Header --> <!-- Begin Content --> <ScrollView Grid.Column="0" Grid.Row="1"> <ContentPresenter VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" /> </ScrollView> <!-- End Content --> </Grid> </ControlTemplate> </ResourceDictionary> </Application.Resources> </Application>

As you can see above, App.xaml is a sort of hybrid between an app configuration and a layout template, it allows you to define a set of application resources and within that, a set of control templates, these templates are what we need to set up our custom header/navigation bar because once you define a ControlTempate with a key, you're then free to write the layout code for that template directly within the control template. You might wonder why I use an Image and then assign a GestureRecognizer instead of just having an image button... Well my method has a little more code but will allow you to rezise images that you assign unlike the image button which will just crop the image if it's too large.

Before we move on, I'll save you the trouble of the 'Application.Resources StaticResource not found for key' error which you would later run into if you do not follow the next step.

Open the original App.cs file and within the constructor for that class, before everything else, add 'InitializeComponent();' as when we get round to testing, this will solve the error I've spoken about above which will save you a bit of head scratching.

Thinking back to when we added App.xaml and we spoke about GestureRecognizers for the back button, well now is when you want to add a method for that button to call. As you've already seen, we called a method named 'OnBackButtonPressed'. App.xaml is the file that will handle this method so paste the following code into it and your back button should work.

public async void OnBackButtonPressed(object sender, EventArgs e)
{
    await Navigation.PopAsync();
}

So that you can confirm you've got yours set up correctly, here's what your App.cs should look like now:

using System;
using Xamarin.Forms;

namespace MyAppNamespace
{
    public partial class App : Application
    {
        public static NavigationPage Navigation = null;

        public App()
        {
// We have to have this here to stop 'Application.Resources StaticResource not found for key error' InitializeComponent(); Navigation = new NavigationPage(new MainPage()); Application.Current.MainPage = Navigation; } protected override void OnStart() { // Handle when your app starts } protected override void OnSleep() { // Handle when your app sleeps } protected override void OnResume() { // Handle when your app resumes }
// Called by the back button in our header/navigation bar. public async void OnBackButtonPressed(object sender, EventArgs e) { await Navigation.PopAsync(); } } }

Now if your app doesn't yet have a main page to load when it starts, now is when you'll want to add it, use the same method we've already covered above for adding a new xaml page to your project, you can even add it under a folder if you want to keep the project tidy (which you should). Notice that in the example above, I create a new MainPage and wrap it in a NavigationPage then set the applications main page to it. This loads the class within the MainPage.xaml file and also allows my app to control navigation using the navigation stack. In case you didn't already know, the navigation stack is used to push and pop pages to and from the users view.

Another thing to look at in the example above is how I set the NavigationPage to a variable in App.cs, some would frown upon this so do it at your own discretion, all this does is allow me to push an pop pages from my navigation stack by calling 'App.Navigation.PopAsync();' instead of '((NavigationPage)Application.Current.MainPage).PopAsync();'

All that's left to do is to go through all the page cs files that this affects and turn off the default header/navigation bar by adding the following line to the page constructor:

NavigationPage.SetHasNavigationBar(this, false);​

And add this line the ContentPage element within the xaml page files:

ControlTemplate="{StaticResource MainPageTemplate}"

Your content pages shuld look something ike this

<!--?xml version="1.0" encoding="utf-8" ?-->
<contentpage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:class="MyAppNamespace.MainPage"
controltemplate="{StaticResource MainPageTemplate}"
backgroundcolor="#FFFFFF"
title="My App">
<stacklayout orientation="Vertical" horizontaloptions="Fill" verticaloptions="FillAndExpand" x:name="mainContainer" spacing="10">
<stacklayout.padding>
<onplatform x:typearguments="Thickness" ios="10, 10, 10, 10" android="10, 10, 10, 10" winphone="10, 10, 10, 10"></onplatform>
</stacklayout.padding>
<label x:name="Title" text="My page title" fontsize="28" textcolor="#000" fontattributes="Bold"></label>
<label x:name="Summary" text="Content here..." fontsize="16"></label>
</stacklayout>
</contentpage>

Conclusion:

So turns out that this is a very simple and neat way to get what a web developer might call a partial view setup in Xamarin, almost like how masterpages used to work where you define the template and then specify where you want the content to load.

Other Resources:

Xamarin forums post that helps with a few tricky bits:
https://forums.xamarin.com/discussion/31872/application-resources-staticresource-not-found-for-key

Brilliant blog post by wolfeprogrammer that helped my understanding of this a lot:
https://wolfprogrammer.com/2016/07/07/custom-app-header-in-forms/


Published at

Tags: Xamarin,C#,Xaml,Android,iOS,Windows Phone

Luke Alderton

Comments

Pavel
Thank you for this article!
17/02/2017
hiren vyas
How hide back image for particular page
23/03/2017
Peter
What happens though if there is nothing in the stack anymore, do you still get the back arrow? How would you control the back arrow so it only pops when it needs to?
14/08/2017
Denis
First, thanks for the article. Second, I have posted a related question regarding this technique and MasterDetailPages over on the Xam forums here: https://forums.xamarin.com/discussion/101870/custom-navigation-bar-with-masterdetailpage#latest I am not sure if you have come across a solution for this issue?
26/08/2017
Ich
Hi Luke! Thanks for the article. Do you have any example how to get data from the underlying page then? For example I want to display the user name if a user is logged in. Could this be done? Many thanks!
26/11/2017
Jose Luis
But.... Navigation.PopAsync() doens't compile at App level, since "Navigation" doesn't exist in that context. What else should be done?
07/01/2018
Luke Alderton
You could try something like '((NavigationPage)Application.Current.MainPage).PopAsync();', providing your mainpage is a NavigationPage or you'll get an error.
17/01/2018
Jonny G
Thank you very useful. One slight mistake, which had me confused for a while, the "c" in the last example's, "controltemplate="{StaticResource MainPageTemplate}" is lower case, other than that great post.
31/01/2018
Jungle
If you put a search bar in the custom header, how would you program the event handler for the search button, as I can't work it out?
05/02/2018
Chico
Great post, saved me a lot of time.
27/02/2018
PS
How can add hamburgermenu
16/04/2018
Trevor
I've added a label and would like to change the text depending on which page is displayed. How can I do that? Thanks.
19/07/2018
Jack
Is it possible to get some source code please?
16/02/2019
Gerry
Thanks - the Xamarin action/navigation bar is AWFUL. Not only does it use way too much space, it is an incredible pain to try to configure. Complete garbage.
11/07/2019
Share with
Tags
Latest Comments
By Mark Gentry on Windows Server 2019 - Change product key does nothing
20 Aug 2021, 03:30 AM
By Cathy on In-Place Upgrade for a Windows Server domain controller
31 Jul 2021, 18:28 PM
By Mr. Greymatter on Raspberry Pi - Running Java app on Raspbian
16 Feb 2021, 07:35 AM
By Mikko Seittenranta on Xamarin Forms multiple instances of same app open
16 Feb 2021, 04:34 AM
By Andrew on Auto/Custom height on Xamarin Forms WebView for Android and iOS
22 Jan 2021, 22:15 PM
By Nick on Raspberry Pi - Running Java app on Raspbian
14 Oct 2020, 19:37 PM
By Ivan on Fixed: Value cannot be null Parameter Name: source
15 Sep 2020, 19:47 PM
By Anand on Raspberry Pi - Bluetooth using Bluecove on Raspbian
7 Sep 2020, 16:53 PM
Categories
App Development
Event
Game Development
Mapping
Modelling
Programming
Review
Robotics
Tutorial
Web Development