Monday, 21 January 2019

Stack Navigation Pattern

There are several ways in which we transition from view to view within a mobile application and often times we leverage one or more of them, here we'll dive into the Stack Navigation Pattern

The stack pattern is a fairly straightforward pattern where pages are stacked on top of each other, each page would have some sort of back button that would in turn let you pop the current page off the stack to return back to the previous page


What this means is that the application holds the current page as well as all previous pages in memory, and then releases each page from memory once it's popped off the stack.

To use the stack pattern we first need to open our App.xaml.cs file


Once you have the open you'll need to instantiate an instance of the navigation page and pass in an instance of your apps root page as a parameter.

using pav.StackNavigation.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace pav.StackNavigation
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            this.MainPage = new NavigationPage(new MainPage());
        }

        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*/ }
    }

}

This will allow us to leverage the stack navigation pattern with minimal effort.

next lets' create a second "Content Page"


this will give us a page to push onto and pop off our navigation stack. Now let's open up our Main Page and add a Push Button.

<?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="pav.StackNavigation.Views.MainPage">
    <StackLayout>
        <!--label-->
        <Label Text="Welcome to Xamarin.Forms!"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
        <!--Push Button-->
        <Button x:Name="Push_Button" 
           Text="Push Page"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
    </StackLayout>

</ContentPage>

Now that we have a push button, lets open up our CodeBehind and add an event receiver to push our SecondPage.xaml onto the navigation stack

using Xamarin.Forms;

namespace pav.StackNavigation.Views
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            Push_Button.Clicked += async (s, e) => await this.Navigation.PushAsync(new SecondPage());
        }
    }
}


Our main page's navigation object is auto-magically wired up to leverage the use of the Navigation stack using dependency injection under the hood.

Next let's open up our Second page and add a pop button

<?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="pav.StackNavigation.Views.SecondPage">
    <ContentPage.Content>
        <StackLayout>
            <!--Label-->
            <Label Text="This is the second page"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="CenterAndExpand" />
            <!--Pop Button-->
            <Button x:Name="Pop_Button"
                Text="Pop this page"
                VerticalOptions="CenterAndExpand"
                HorizontalOptions="CenterAndExpand" />   
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

now that we have our pop button let's wire it up in the codebehind. 

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace pav.StackNavigation.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
       public partial class SecondPage : ContentPage
       {
              public SecondPage ()
              {
                     InitializeComponent ();
            Pop_Button.Clicked += async (s,e) => await this.Navigation.PopAsync();
              }
    }
}

and that's it we're now using a stack navigation with minimal effort.

Navigation bar Customization

To modify the look and feel of the navigation bar we have to go back to our app.xaml.cs page.

using pav.StackNavigation.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace pav.StackNavigation
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

            this.MainPage = new NavigationPage(new MainPage()) {
                BarBackgroundColor = Color.Maroon,
                BarTextColor = new Color(256, 256, 256),
                BackgroundColor = new Color(256, 0, 0, .2)
            };
        }

        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*/ }
    }
}

with the above modifications made our app we see the following changes:


now these changes are consistent throughout our entire navigation model, so no matter where we are in our navigation stack we'll have a consistent theme.

You may have noticed that in the above our navigation bar has a page title, this can be accomplished declarative-ly in xaml  or programatically in the code-behind

<?xml version="1.0" encoding="utf-8" ?>
   
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             Title="Home"
             x:Class="pav.StackNavigation.Views.MainPage">
    <StackLayout>
        <!--label-->
        <Label Text="Welcome to Xamarin.Forms!"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
        <!--Push Button-->
        <Button x:Name="Push_Button" 
           Text="Push Page"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Finally we can override a back button being pressed, generally we'd never want to do this because it could lead to confusion, with the user thinking that your app has frozen or isn't working correctly because it breaks an established paradigm, but with every rule there's always that one exception, anyway to do this you'd simple override the OnBackButtonPressed function.

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace pav.StackNavigation.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SecondPage : ContentPage
    {
        public SecondPage()
        {
            InitializeComponent();
            Pop_Button.Clicked += async (s, e) => await this.Navigation.PopAsync();
            NavigationPage.SetHasBackButton(this, false);
        }

        protected override bool OnBackButtonPressed()
        {
            return true; // block back button
            return false; // normal back button behavoir
        }
    }
}

with overriding the OnBackButtonPressed behavior we disabled the back button functionality and in the constructor we hid the virtual back button, in this situation we have to handle poping our page off our navigation stack ourselves, which we previously did in the Pop_button's event handler.