Skip to content

07 Navigation

Matthew Leibowitz edited this page Apr 16, 2019 · 4 revisions

In this section we are going to have a look at different types of navigation. There are many ways to move between pages in Xamarin.Forms, and each has their own advantages.

You can view the code and the diff for this step on GitHub.

Adding a second page

Now that we have our basic launcher page done, we want to add the actual game page and be able to get to it. This is very simple to do:

  1. Right-click the .NET Standard project
  2. Select Add | New Item...
  3. Select Content Page
  4. Enter a page name: GamePage.xaml

We should now have a new page, and you can go ahead and remove the default template controls so we are left with an empty <ContentPage> element:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TicTacToe.GamePage">

</ContentPage>

Building this page is fairly straight forward, using what we learnt in the previous sections. We can construct the page using various layouts and controls.

Our page is going to have three main areas:

  1. A game board from a grid of buttons
  2. An "I give up!" button to take us back to the launcher page
  3. A small label that will show who needs to play

But, before we actually go ahead and create the page, let's have a look at how we can get to this page from the launcher page.

There is lots more information on navigation in the docs.

Basic navigation

If we have a look at the game's code behind (App.xaml.cs), we can see that the launcher page is set as the main page in the constructor:

public App()
{
    InitializeComponent();

    MainPage = new MainPage();
}

This is the simplest form of navigation. So, if we want to navigate to the game page, we can just set this property to a new GamePage instance. To do this, we can go to the "play" button's Clicked event handler and do it:

private void OnPlayClicked(object sender, EventArgs e)
{
    App.Current.MainPage = new GamePage();
}

That is it! When we run the app, clicking the button will take us to the game page.

This is great, but new there is no way to go back to the launcher page. And, if we are on an Android or Windows device, the back button quits the game. This is obviously not very cool, so we can do something to fix this.

The first thing we can do is add a button to the game page that will take us back to the launcher page.

<Button Text="Back" Clicked="OnBackClicked"
        HorizontalOptions="Center" VerticalOptions="Center" />

And, like any good game, we should first confirm this (the player might be so excited to play that they just hit the back button accidentally). To do this check, we can make use of the alert again:

private async void OnBackClicked(object sender, EventArgs e)
{
    var message = "Are you sure you want to leave the game? You might win!";
    if (await DisplayAlert("Back", message, "Yes", "No"))
    {
        App.Current.MainPage = new MainPage();
    }
}

We are using an overload of the DisplayAlert method that we used to show the "in progress" message on the launcher page. This overload takes an additional parameter and returns a Task<bool> which we can use to determine if the player tapped the "yes" button or the "no" button.

The navigation that we are using here is not typically used in apps as it involves overriding most of the default functionality. We will be changing this later.

Intercepting the back button presses

Although we now have logic to go back to the launcher page, pressing the device's back button still quits the app.

To intercept this event, we can make use of a method on the page itself. We can override the OnBackButtonPressed method of the page and do some checks there. For our particular case in this page, we always want to prevent the default action.

So, instead of duplicating logic, we can refactor the OnBackClicked method and extract the logic into a new method:

protected override bool OnBackButtonPressed()
{
    DoGoBack();

    // we did intercept the default action
    return true;
}

private void OnBackClicked(object sender, EventArgs e)
{
    DoGoBack();
}

private async void DoGoBack()
{
    var message = "Are you sure you want to leave the game? You might win!";
    if (await DisplayAlert("Back", message, "Yes", "No"))
    {
        App.Current.MainPage = new MainPage();
    }
}

Navigation pages

The first type of navigation we will look at is the use of the NavigationPage. This is what we will be using in the final game. To use this page, we don't actually change our page type, rather we "wrap" the page in a NavigationPage.

Instead of changing the MainPage directly, we use the Navigation property of all pages to request types of navigation. If we want to go to a page, we use the PushAsync method. When we want to go back, we use the PopAsync method.

To switch to navigation using the NavigationPage, we need to change two lines that directly change the MainPage of the app. This is because we want to use the default system implementation of navigation. The first thing to change is to change the OnPlayClicked method of the launcher page:

// App.Current.MainPage = new GamePage();
Navigation.PushAsync(new GamePage());

And similarly the DoGoBack method of the game page:

// App.Current.MainPage = new MainPage();
Navigation.PopAsync();

Finally, to make all the navigation actually work, we change the constructor in the App.xaml.cs file to "wrap" the launcher page in a NavigationPage, and pass the launcher page as a parameter:

public App()
{
    InitializeComponent();

    MainPage = new NavigationPage(new MainPage());
}

If we run the game, we can see a thick blue navigation bar at the top of both pages. This is where the page title and navigation buttons would be. But, because we don't want this bar, we can just hide it.

To hide the navigation bar, we can make use of an attached property on the pages where we don't want the bar. So, in our game, we will add the NavigationPage.HasNavigationBar attached property to the launcher page and the game page:

<ContentPage NavigationPage.HasNavigationBar="False" />

When we run the game, we can see that the navigation bar is gone and we have a full screen to ourselves:

You can read more about navigation pages in the docs.

Other navigation paradigms

Although we now have what we want for our game, we also need to know about other navigation paradigms.

Master-detail pages

Another common navigation paradigm is a master-detail, or the "hamburger menu". This paradigm builds on top of the NavigationPage to add a menu to the left of the main app.

Because we still use the NavigationPage, we still use the Navigation property with the PushAsync and PopAsync to do navigation, but we now have an area to add global menu items.

MainPage = new MasterDetailPage
{
    Master = new MainPage { Title = "Master" },
    Detail = new NavigationPage(new MainPage())
};

You can read more about master-detail pages in the docs.

Tabbed pages

One final navigation paradigm that we will look at is the tabbed page. This is a page that has multiple child pages, and creates a tab for each page.

MainPage = new TabbedPage
{
    Children =
    {
        new MainPage { Title = "Page 1" },
        new MainPage { Title = "Page 2" },
    }
};

You can read more about tabbed pages in the docs.

Clone this wiki locally