Mobiele apps programmeren met Xamarin – Navigatie – Hiërarchische Navigatie

print

Inhoud


Wat vooraf ging


De “stack”

Navigating through a stack of pages

Hiërarchische Navigatie is de navigatie door een “stack” (stapel) van pagina’s. U navigeert naar een nieuwe pagina die bovenop de “stack” komt te liggen en u gebruikt de Back-knop om weer te keren naar de vorige pagina. De navigatie gebeurt op basis van het LIFO-principe (Last In First Out).

Als u van de ene pagina naar de andere navigeert zal de app een nieuwe pagina Pushen bovenop de navigatiestapel. Deze nieuwe pagina wordt dan de actieve pagina.

Als u terugkeert naar de vorige pagina zal de applicatie the huidige pagina Poppen van de navigatiestapel en de onderliggende pagina wordt de actieve pagina.

De navigatie (terugkeren) gebeurt met de ingebouwde Back-knop van het besturingsysteem.

Navigeren gebeurt bij voorkeur op basis van ContentPages.


Navigeer de “stack”

Onderstaande voorbeeld demonstreert hoe u navigeert doorheen de “stack”.

<?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="Navigatie.Pagina1" Title="Pagina 1">
    <StackLayout>
        <Button x:Name="btnVolgende" Text="Volgende pagina" VerticalOptions="CenterAndExpand" Clicked="btnVolgende_Clicked" />
    </StackLayout>

</ContentPage>

Vergeet de toekenning van de titel niet via Title="Pagina 1".

  • Maak deze pagina de startpagina van de applicatie via App.xaml.cs.
using Xamarin.Forms;

namespace Navigatie
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

            MainPage = new Pagina1();
        }

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

Root

Deze pagina is echter nog geen navigatiepagina. Om er een navigatie van te maken moet de pagina omgezet worden naar een NavigationPage, dit gebeurt via new NavigationPage(new Pagina1()). Deze pagina wordt vervolgens de Root-pagina genoemd.

using Xamarin.Forms;

namespace Navigatie
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

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

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

Merk de titel op bovenaan de pagina.

Voor we naar een volgende pagina kunnen overgaan moeten we deze eerst aanmaken.

  • Maak onderstaande ContentPage (met de naam Pagina2) aan via XAML.
<?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="Navigatie.Pagina2" Title="Pagina 2">
    <StackLayout>
        <Button x:Name="btnVorige" Text="Vorige pagina" VerticalOptions="CenterAndExpand" Clicked="btnVorige_Clicked" />
        <Button x:Name="btnVolgende" Text="Volgende pagina" VerticalOptions="CenterAndExpand" Clicked="btnVolgende_Clicked" />
    </StackLayout>
</ContentPage>

Vergeet ook hier de titel niet Title="Pagina 2".

Push pagina bovenaan de “stack”

Om deze nieuwe pagina te zien moet deze worden gePush op de “stack”. dit gebeurt via await Navigation.PushAsync(new Pagina2());. Merk op dat deze code asynchroon is. We voegen deze code toe aan het klikken op de Volgende-knop op Pagina1.

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina1 : ContentPage
    {
        public Pagina1()
        {
            InitializeComponent();
        }

        async private void btnVolgende_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Pagina2());
        }
    }
}

Vergeet niet ook de event asynchroon te maken met async.

Wanner u naar deze tweede pagina navigeert ziet u, bij Android, bovenaan de Back-knop die u kunt gebruiken om weer te keren naar de eerste pagina.

Animatie

Misschien, als u heel goed oplet, hebt u enige animatie opgemerkt bij het laden van de tweede pagina. Wilt u deze animatie niet dan kunt u deze uitschakelen door de tweede parameter van functie public Task PushAsync (Page page, Boolean animated) op false te zetten. Deze animatieparameter is ook in volgende onderstaande voorbeelden beschikbaar maar ga ik niet telkens herhalen.

await Navigation.PushAsync(new Pagina2(), false);

Pop de huidige pagina van de “stack”

Onze tweede pagina bevat niet enkel bovenaan de Back-knop maar ook een eigen knop die ons moet terugbrengen naar de vorige pagina. Dit gebeurt door de huidige pagina te Poppen await Navigation.PopAsync();. Dit gebeurt ook asynchroon. De code van Pagina2 is nu:

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina2 : ContentPage
    {
        public Pagina2()
        {
            InitializeComponent();
        }

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

        private void btnVolgende_Clicked(object sender, EventArgs e)
        {
            
        }
    }
}

De “stack” manipuleren

  • Om wat meer gevorderde methoden voor het manipuleren van de “stack” te bespreken voegen we nog een Pagina3 en een Pagina2a toe met respectievelijk onderstaande XAML.

Pagina3

<?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="Navigatie.Pagina3" Title="Pagina 3">
    <StackLayout>
        <Button x:Name="btnVorige" Text="Vorige pagina" VerticalOptions="CenterAndExpand" Clicked="btnVorige_Clicked" />
        <Button x:Name="btnNaarRoot" Text="Terug naar de Root" VerticalOptions="CenterAndExpand" Clicked="btnNaarRoot_Clicked" />
        <Button x:Name="btnInvoegen" Text="Een Pagina invoegen" VerticalOptions="CenterAndExpand" Clicked="btnInvoegen_Clicked" />
        <Button x:Name="btnVerwijder" Text="Verwijder pagina 2" VerticalOptions="CenterAndExpand" Clicked="btnVerwijder_Clicked" />
    </StackLayout>
</ContentPage>

Pagina2a

<?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="Navigatie.Pagina2a" Title="Pagina 2a">
    <StackLayout>
        <Label x:Name="lblTitel" Text="U komt van pagina..." VerticalOptions="CenterAndExpand" />
        <Button x:Name="btnVorige" Text="Vorige pagina" VerticalOptions="CenterAndExpand" Clicked="btnVorige_Clicked" />
    </StackLayout>
</ContentPage>

  • Voeg op pagina2 de code toe die ons brengt naar pagina3 door het klikken op de Volgende-knop.
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina2 : ContentPage
    {
        public Pagina2()
        {
            InitializeComponent();
        }

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

        async private void btnVolgende_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Pagina3());
        }
    }
}
  • Voeg de events toe op Pagina3.
  • De code voor de Vorige-knop kunt u reeds toevoegen await Navigation.PopAsync().
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina3 : ContentPage
    {
        public Pagina3()
        {
            InitializeComponent();
        }

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

        private void btnNaarRoot_Clicked(object sender, EventArgs e)
        {

        }

        private void btnInvoegen_Clicked(object sender, EventArgs e)
        {

        }

        private void btnVerwijder_Clicked(object sender, EventArgs e)
        {

        }
    }
}

Naar de Root

Terugkeren naar de Root gebeurt via volgende code await Navigation.PopToRootAsync(). Ook deze code is asynchroon.

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina3 : ContentPage
    {
        public Pagina3()
        {
            InitializeComponent();
        }

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

        async private void btnNaarRoot_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopToRootAsync();
        }

        private void btnInvoegen_Clicked(object sender, EventArgs e)
        {

        }

        private void btnVerwijder_Clicked(object sender, EventArgs e)
        {

        }
    }
}

Invoegen tussen andere pagina’s

Het is niet enkel mogelijk om pagina’s bovenop de “stack” te leggen (Push) of van bovenop de “stack” te verwijderen (Pop), er kunnen ook pagina’s worden tussengevoegd.

De code hiervoor is public Void InsertPageBefore (Page page, Page before) waarbij de eerste parameter de pagina is die moet worden ingevoegd en de tweede parameter de pagina waarvoor moet worden ingevoegd (naar de huidige pagina kan verwezen worden met this.

Onderstaande code demonstreert dit maar voegt enkel in indien de pagina nog niet ingevoegd is. Om te kijken of de pagina al bestaat kan volgende code gebruikt worden Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2a"). Dit is een LINQ-functie die dus ook toegankelijk moet worden gemaakt via using System.Linq;.

De aangepaste code wordt dus:

using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina3 : ContentPage
    {
        public Pagina3()
        {
            InitializeComponent();
        }

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

        async private void btnNaarRoot_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopToRootAsync();
        }

        private void btnInvoegen_Clicked(object sender, EventArgs e)
        {
            var page2a = Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2a");
            if (page2a == null)
            {
                Navigation.InsertPageBefore(new Pagina2a(), this);
            }
        }

        private void btnVerwijder_Clicked(object sender, EventArgs e)
        {

        }
    }
}

Als u eerst deze pagina invoegt en u klikt dan op de Vorige-knop dan komt u op Pagina2a terecht. Klikt u nu nog eens op de Vorige-knop (op pagina 2a) dan gaat u naar Pagina2.

Een pagina verwijderen

De huidige pagina kunt u van de “stack” halen via Pop. Maar u kunt ook een andere pagina dan de huidige pagina uit de “stack” verwijderen.

De code hiervoor is public Void RemovePage (Page page). U kunt het best eerst controleren of deze pagina reeds toegevoegd is aan de “stack” Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2").

using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina3 : ContentPage
    {
        public Pagina3()
        {
            InitializeComponent();
        }

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

        async private void btnNaarRoot_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopToRootAsync();
        }

        private void btnInvoegen_Clicked(object sender, EventArgs e)
        {
            var page2a = Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2a");
            if (page2a == null)
            {
                Navigation.InsertPageBefore(new Pagina2a(), this);
            }

        }

        private void btnVerwijder_Clicked(object sender, EventArgs e)
        {
            var page2 = Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2");
            if (page2 != null)
            {
                Navigation.RemovePage(page2);
            }

        }
    }
}

Gegevens doorgeven aan een andere pagina via de constructor

Zoals u reeds opmerkte is het de bedoeling dat op pagina2a wordt aangegeven van welke pagina u komt.

Hiertoe gaan we eerst de constructor van pagina2a aanpassen zodat deze een tekst, een titel kan ontvangen en vervolgens gepast weergeven in de hiertoe voorziene label. De code van pagina2a wordt nu:

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina2a : ContentPage
    {
        public Pagina2a(string Titel = "...")
        {
            InitializeComponent();
            lblTitel.Text = "U komt van " + Titel + ".";
        }

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

U kan nu een titel meegeven (indien u dit niet doet worden er “…” getoond).

We kunnen dus ook de code die pagina2a aanroept wijzigen zodat we de titel kunnen meegeven. U kunt de titel van de huidige pagina meegeven met this.Title.

using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina3 : ContentPage
    {
        public Pagina3()
        {
            InitializeComponent();
        }

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

        async private void btnNaarRoot_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopToRootAsync();
        }

        private void btnInvoegen_Clicked(object sender, EventArgs e)
        {
            var page2a = Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2a");
            if (page2a == null)
            {
                Navigation.InsertPageBefore(new Pagina2a(this.Title), this);
            }

        }

        private void btnVerwijder_Clicked(object sender, EventArgs e)
        {
            var page2 = Navigation.NavigationStack.FirstOrDefault(p => p.Title == "Pagina 2");
            if (page2 != null)
            {
                Navigation.RemovePage(page2);
            }

        }
    }
}

Gegevens doorgeven aan een andere pagina via binding

U kunt ook code naar een volgende pagina meegeven via Binding. Omdat een praktisch voorbeeld niet echt past binnen het kader van deze oefening geeft ik hier enkel voorbeeldcode zoals die ook op de officiële pagina te vinden is.

async void OnNavigateButtonClicked (object sender, EventArgs e)
{
  var contact = new Contact {
    Name = "Jane Doe",
    Age = 30,
    Occupation = "Developer",
    Country = "USA"
  };

  var secondPage = new SecondPage ();
  secondPage.BindingContext = contact;
  await Navigation.PushAsync (secondPage);
}

Merk op dat hiertoe reeds een Contact-klasse moet bestaan. Hoe u dit moet doen leest u in mijn post over klassen.

Vervolgens wordt eerst de pagina gedeclareerd.

var secondPage = new SecondPage ();

Dan wordt de nieuwe contact toegekend aan de BindingContext van de nieuwe pagina. Dit kan want deze nieuwe pagina bestaat al in het geheugen van de computer.

secondPage.BindingContext = contact;

Pas nu wordt de pagina gePusht.

await Navigation.PushAsync (secondPage);

Deze secondPage bevat dan de nodige bindings.

<Label Text="{Binding Name}" />

Indien u een lijst van contactpersonen zou weergeven in een ListView kunt u onderstaande code-variant gebruiken.

async void OnItemSelected (object sender, SelectedItemChangedEventArgs e)
{
  if (listView.SelectedItem != null) {
    var detailPage = new DetailPage ();
    detailPage.BindingContext = e.SelectedItem as Contact;
    listView.SelectedItem = null;
    await Navigation.PushAsync (detailPage);
  }
}

Modale pagina’s

Een modale pagina verschilt van een gewone pagina in het feit dat u een gewone pagina niet kunt verlaten met de Back-knop. Modale pagina’s worden dan ook gebruikt indien er van de gebruiker verplichte acties/invoer verwacht wordt. De pagina blijft bovenaan de “stack” liggen tot de gebruiker de gewenste actie/invoer volbracht heeft en via een daartoe voorziene knop de pagina kan verlaten.

Concreet, een modale pagina mist de standaard Back-knop van de gewone pagina.

De code om een modale pagina te Pushen is await PushModalAsync(Page page).
De code om een modale pagina te Poppen is await Navigation.PopModalAsync().

Om dit te demonstreren passen we de code van Pagina1 aan om Pagina2 nu modaal weer te geven.

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina1 : ContentPage
    {
        public Pagina1()
        {
            InitializeComponent();
        }

        async private void btnVolgende_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushModalAsync(new Pagina2());
        }
    }
}

Op Pagina2 moeten we nu ook de code aanpassen om weer te keren naar await Navigation.PopModalAsync().

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina2 : ContentPage
    {
        public Pagina2()
        {
            InitializeComponent();
        }

        async private void btnVorige_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopModalAsync();
        }

        async private void btnVolgende_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Pagina3());
        }
    }
}

Merk op dat Pagina2 nu niet langer meer de titel en de Back-knop weergeeft.

De standaard Back-knop uitschakelen

Ook al is Pagina2 modaal, en verschijnt er geen Back-knop bovenaan, u kunt nog steeds terugkeren gebruikmakend van de Back-knop van uw Android-toestel zelf! Dus eigenlijk wordt op deze manier het modale effect omzeild.

De oplossing is de werking van de Back-knop van uw Android-toestel te overschrijven zodat deze niets meer doet. Dit gebeurt met onderstaande code:

protected override bool OnBackButtonPressed() {
    return true;
}

De event OnBackButtonPressed() is een ingebouwde functie. Door deze enkel return true; te laten returnen schakelen we deze eigenlijk uit.

De volledige code van Pagina2 is nu:

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

namespace Navigatie
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Pagina2 : ContentPage
    {
        public Pagina2()
        {
            InitializeComponent();
        }

        async private void btnVorige_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopModalAsync();
        }

        async private void btnVolgende_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Pagina3());
        }

        protected override bool OnBackButtonPressed() {
            return true;
        }
    }
}


Behandelde Basiscompetenties uit de module ICT Programmeren – Integratie externe functionaliteiten

  • IC BC017 – kan ICT veilig en duurzaam gebruiken
  • IC BC256 – kan diverse elementen tot een nieuw betekenisvol geheel samenstellen
  • IC BC288 – kan ICT-problemen oplossen

Geef een reactie

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.