Mobiele apps programmeren met Xamarin – De control flow van een C#-programma – invoer en uitvoer

print

Inhoud


Wat vooraf ging

Deze post sluit aan bij de gelijkaardige post uit Start to program – Control Flow. We hernemen dezelfde oefeningen maar werken deze nu uit als een mobiele app in C#. U kunt een Xamarin-project opstarten en u hebt al kennisgemaakt met XAML en weet hoe functies en events worden gemaakt.

We gaan een project aanmaken dat uit meerdere onderdelen bestaat, we gaan eigenlijk meerdere voorbeelden, elk op hun eigen pagina, integreren tot één app.

Merk op dat er 4 tabbladen gemaakt zijn, ieder tabblad zal zijn eigen pagina en oefening bevatten.

Om het overzichtelijk te houden ga ik dit opdelen in 3 posts.

  1. In- en uitvoer met BTW-berekenen als voorbeeld.
  2. Selecties waarin de 3 andere pagina’s worden uitgewerkt.
  3. Herhalingen met een paar losstaande voorbeelden.

Pagina’s toevoegen en tabbladen aanmaken

Laten we eerst alles klaar zetten, het project starten, de pagina’s toevoegen en de tabbladen aanmaken.

  • Start een nieuw Xamarin-project (ik heb het de naam Control Flow gegeven).
  • Het tabblad bevat 4 tabbladen en dus ook 4 pagina’s. Voeg deze 4 pagina reeds toe. Ik gebruikte de namen BTWBerekenen, Pos_Neg_Nul, Geslaagd, KleurenSelecteren.

Hieronder vindt u nog eens hoe u een pagina kunt toevoegen.

  • Klik rechts op het basisproject (hier App1 (Portable)).
  • Klik op Add – New Item ….

  • Kies welk item u wenst toe te voegen. Bijvoorbeeld een nieuw Forms Blank Content Page XAML.
  • Kies de gewenste naam.
  • Klik op Add.

  • Klik op Add.
  • Er is een pagina (hier Page1.xaml) en een code-behind pagina (hier Page1.xaml.cs) toegevoegd.

  • Voeg op deze manier de 4 pagina’s BTWBerekenen, Pos_Neg_Nul, Geslaagd, KleurenSelecteren toe.

Nu moeten deze 4 pagina’s toegevoegd worden in een tabblad.

  • Open het bestand App.xaml.cs.
  • De reeds aangemaakte code ziet u hieronder:
using Xamarin.Forms;

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

            MainPage = new Control_Flow.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
        }
    }
}
  • U past de code als volgt aan:
using Xamarin.Forms;

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

            var tabs = new TabbedPage();
            tabs.Children.Add(new BTWBerekenen { Title = "BTW Berekenen" });
            tabs.Children.Add(new Pos_Neg_Nul { Title = "Positief of negatief" });
            tabs.Children.Add(new Geslaagd { Title = "Geslaagd" });
            tabs.Children.Add(new KleurenSelecteren { Title = "Kleuren" });
            MainPage = tabs;

        }

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

Een woordje uitleg.

U maakt een variabele aan die een nieuwe TabbedPage() initialiseert.

var tabs = new TabbedPage();

Vervolgens moet u tabbladen (“children”) toevoegen aan deze pagina. U voegt een nieuwe instantie van een aangemaakte pagina toe en stelt de propertie Title in, die de tekst krijgt die u wenst weer te geven bovenaan het tabblad. Dit herhaalt u voor iedere pagina die u wenst toe te voegen aan het tabblad.

tabs.Children.Add(new BTWBerekenen { Title = "BTW Berekenen" });

Uiteindelijk kent u deze TabbedPage toe aan de MainPage zodat deze wordt opgestart als hoofdpagina.

MainPage = tabs;

Nu bent u klaar om de eigenlijke pagina’s te ontwerpen.


Invoeren, verwerken en uitvoeren

De control flow is in essentie vaak te herleiden tot volgende 3 stappen:

  1. Gegevens invoeren.
  2. De ingevoerde gegevens verwerken tot informatie.
  3. Informatie uitvoeren.

Als flow chart (zie algoritmes) zie dit er als volgt uit.

Wat is informatie?

Informatie kan gezien worden als het resultaat van verwerkte gegevens.

Bijvoorbeeld:

45 is een gegeven (of data). Maar waar staat die 45 nu eigenlijk voor?

Na verwerking van het gegeven 45 kan bv. blijken dat de 45 staat voor behaalde punten en dat dit onvoldoende is om geslaagd te zijn.

Onvoldoende om geslaagd te zijn is dan de informatie die gehaald is uit het gegeven 45.

Invoer

De invoer in Xamarin wordt vaak gedaan via:

Uitvoer

De uitvoer gebeurt vaak via:

Sommige van deze in- en uitvoerobjecten komen in deze post reeds aan bod, anderen komen later wel.

Voorbeeld: BTW Berekenen

  • De gebruiker moet een getal invoeren.
  • Uit een keuzelijst het gewenste BTW-percentage selecteren.
  • Na het klikken op een knop/button moet de berekende BTW getoond worden.

Maak het onderstaand scherm aan:

De invoer

Gebruik een numeriek toetsenbord om het bedrag in te geven.

Gebruik een keuzelijst om het BTW-percentage te kiezen.

De XAML

De XAML om de bovenstaande pagina aan te maken vindt u hieronder. Ik ga eerst de volledige XAML tonen en nadien in detail bespreken.

<?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="Control_Flow.BTWBerekenen">
    <StackLayout Margin="10">
        <Label Text="BTW Berekenen" FontSize="Large" HorizontalTextAlignment="Center"></Label>
        <StackLayout Orientation="Horizontal">
            <Label Text="Bedrag zonder BTW:" HeightRequest="40" WidthRequest="150" VerticalTextAlignment="Center"></Label>
            <Entry x:Name="entryInvoer" Placeholder="Bedrag" HorizontalOptions="FillAndExpand" Keyboard="Numeric" ></Entry>
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Percentage:" HeightRequest="40" WidthRequest="150" VerticalTextAlignment="Center"></Label>
            <Picker x:Name="pickPercentage" Title="Percentage" HorizontalOptions="FillAndExpand">
                <Picker.ItemsSource>
                    <x:Array Type="{x:Type x:String}">
                        <x:String>0</x:String>
                        <x:String>6</x:String>
                        <x:String>12</x:String>
                        <x:String>21</x:String>
                    </x:Array>
                </Picker.ItemsSource>
            </Picker>
        </StackLayout>
        <Button Text="Berekenen" Clicked="Button_Clicked"></Button>
        <StackLayout Orientation="Horizontal">
            <Label Text="BTW:" WidthRequest="150" VerticalTextAlignment="Center" HeightRequest="30" ></Label>
            <Label x:Name="lblUitvoer" HorizontalOptions="FillAndExpand" BackgroundColor="Red" TextColor="Yellow" HeightRequest="30" VerticalTextAlignment="Center" ></Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

De basis van de pagina is een StackLayout met een Margin="10" om wat afstand van de rand te behouden.

<?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="Control_Flow.BTWBerekenen">
    <StackLayout Margin="10">
    </StackLayout>
</ContentPage>

Bovenaan komt er Label die groter is FontSize="Large" en horizontaal gecentreerd HorizontalTextAlignment="Center".

<Label Text="BTW Berekenen" FontSize="Large" HorizontalTextAlignment="Center"></Label>

Om de tekst “Bedrag zonder BTW:” en de invoer naast mekaar te plaatsen heb ik gekozen, er zijn ook andere mogelijkheden maar die zien we later, voor een StackLayout met een horizontale oriëntatie Orientation="Horizontal".

        <StackLayout Orientation="Horizontal">
        </StackLayout>

Deze StackLayout bevat een Label met een ingestelde hoogte HeightRequest="40", breedte WidthRequest="150" en met de tekst verticaal gecentreerd VerticalTextAlignment="Center", dit allemaal om het te laten uitkomen met de achterliggende Entry. Merk op dat dit een request is, met andere woorden, het kan zijn dat de layout er voor kiest dit te negeren.

De Entry krijgt een naam x:Name="entryInvoer" want hij dient gebruikt te worden in de code-behind (u herinnert zich wellicht dat de prefix x: verwijst naar de namespace xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
), een placeholder Placeholder="Bedrag" om aan te duiden welke invoer verwacht wordt en de Entry moet de beschikbare ruimte horizontaal opvullen HorizontalOptions="FillAndExpand". Aangezien we enkel een getal mogen invoeren wordt het numerieke toetsenbord gekozen Keyboard="Numeric".

        <StackLayout Orientation="Horizontal">
            <Label Text="Bedrag zonder BTW:" HeightRequest="40" WidthRequest="150" VerticalTextAlignment="Center"></Label>
            <Entry x:Name="entryInvoer" Placeholder="Bedrag" HorizontalOptions="FillAndExpand" Keyboard="Numeric" ></Entry>
        </StackLayout>

Vervolgens komt er weer een horizontaal blokje met deze keer een Label en een Picker.

        <StackLayout Orientation="Horizontal">
            <Label Text="Percentage:" HeightRequest="40" WidthRequest="150" VerticalTextAlignment="Center"></Label>
            <Picker x:Name="pickPercentage" Title="Percentage" HorizontalOptions="FillAndExpand">
                <Picker.ItemsSource>
                    <x:Array Type="{x:Type x:String}">
                        <x:String>0</x:String>
                        <x:String>6</x:String>
                        <x:String>12</x:String>
                        <x:String>21</x:String>
                    </x:Array>
                </Picker.ItemsSource>
            </Picker>
        </StackLayout>

De Label mag geen probleem meer zijn, laten we dus even stilstaan bij de Picker (ik geef toe dat deze code niet de meeste leesbare is).

De Picker moet gebruikt worden in code-behind en krijgt dus ook een naam x:Name="pickPercentage", hij heeft ook een titel Title="Percentage" en dient de beschikbare ruimte horizontaal op te vullen HorizontalOptions="FillAndExpand".

De Picker heeft een aantal items nodig, die de lijst uitmaken Picker.ItemsSource. Deze items zijn een array (een lijst) van een bepaald type x:Array Type="{x:Type x:String}". De elementen van de array zijn van hetzelfde type en worden één voor één toegevoegd. De prefix x: verwijst naar de namespace xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
).

            <Picker x:Name="pickPercentage" Title="Percentage" HorizontalOptions="FillAndExpand">
                <Picker.ItemsSource>
                    <x:Array Type="{x:Type x:String}">
                        <x:String>0</x:String>
                        <x:String>6</x:String>
                        <x:String>12</x:String>
                        <x:String>21</x:String>
                    </x:Array>
                </Picker.ItemsSource>
            </Picker>

Vervolgens wordt een Button toegevoegd die de Clicked="Button_Clicked"-event bevat.

        <Button Text="Berekenen" Clicked="Button_Clicked"></Button>

De onderstaande XAML die de uitvoer verzorgt mag normaal geen geheimen meer bevatten.

        <StackLayout Orientation="Horizontal">
            <Label Text="BTW:" WidthRequest="150" VerticalTextAlignment="Center" HeightRequest="30" ></Label>
            <Label x:Name="lblUitvoer" HorizontalOptions="FillAndExpand" BackgroundColor="Red" TextColor="Yellow" HeightRequest="30" VerticalTextAlignment="Center" ></Label>
        </StackLayout>

De code-behind

Ga naar de code-behind, die vindt u in het bestand BTWBerekenen.xaml.cs.

De reeds aangemaakte ziet u hieronder.

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

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

        private void Button_Clicked(object sender, EventArgs e)
        {

        }
    }
}

Merk onderstaande code op:

[XamlCompilation(XamlCompilationOptions.Compile)]

XamlCompilation is een, op het ogenblik van dit schrijven, recente toevoeging aan Xamarin die prestaties van Xamarin moet verbeteren. We laten deze dus gewoon staan.

Invoer

Eerst wordt de invoer verzorgt. Hiertoe worden de nodige variabelen gedeclareerd en ingevoerde waarden worden toegekend met de nodige conversie (want C# zal de ingevoerde tekst niet automatisch kunnen converteren naar het gewenste type). Dit gebeurt, uiteraard, allemaal nadat er op de knop gedrukt is.

private void Button_Clicked(object sender, EventArgs e)
{
    decimal bedrag = 0;
    Int16 percentage = 0;
    decimal btw = 0;

    //invoer
    bedrag = Convert.ToDecimal(entryInvoer.Text);
    percentage = Convert.ToInt16(pickPercentage.SelectedItem.ToString());

}

Bekijk nog even de code die nodig is om het geselecteerde item uit de Picker toe te kennen.

percentage = Convert.ToInt16(pickPercentage.SelectedItem.ToString());

Berekening

Vervolgens komt de berekening. Ik herneem een voorbeeld uit een vorige post waarin we reeds de BTW berekend hebben via een functie (u vindt de uitleg in deze post).

public decimal BTW(int bedrag, int perc = 21)
{
    decimal resultaat = bedrag * perc / 100;
    return resultaat;
}

We gaan echter een kleine aanpassing moeten uitvoeren, het bedrag kan immers decimaal zijn, we moeten dus het type van de parameter bedrag in de functie wijzigen naar decimal.

public decimal BTW(decimal bedrag, int perc = 21)
{
    decimal resultaat = bedrag * perc / 100;
    return resultaat;
}

Deze functie wordt aangeroepen en het teruggegeven resultaat wordt in de voorziene variabele btw gestoken.

btw = BTW(bedrag, percentage);

De aangepaste code is nu:

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

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

        private void Button_Clicked(object sender, EventArgs e)
        {
            decimal bedrag = 0;
            Int16 percentage = 0;
            decimal btw = 0;

            //invoer
            bedrag = Convert.ToDecimal(entryInvoer.Text);
            percentage = Convert.ToInt16(pickPercentage.SelectedItem.ToString());

            //berekening
            btw = BTW(bedrag, percentage);

        }

        public decimal BTW(decimal bedrag, int perc = 21)
        {
            decimal resultaat = bedrag * perc / 100;
            return resultaat;
        }

    }
}

Uitvoer

Tenslotte wordt het resultaat, dat zich bevindt in de variabele btw toegekend aan de hiertoe voorziene Label lblUitvoer. De uitvoer wordt geformatteerd op twee cijfers na de komma.

lblUitvoer.Text = btw.ToString("F2")

De aangepaste code wordt nu:

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

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

        private void Button_Clicked(object sender, EventArgs e)
        {
            decimal bedrag = 0;
            Int16 percentage = 0;
            decimal btw = 0;

            //invoer
            bedrag = Convert.ToDecimal(entryInvoer.Text);
            percentage = Convert.ToInt16(pickPercentage.SelectedItem.ToString());

            //berekening
            btw = BTW(bedrag, percentage);

            //uitvoer
            lblUitvoer.Text = btw.ToString("F2");

        }

        public decimal BTW(decimal bedrag, int perc = 21)
        {
            decimal resultaat = bedrag * perc / 100;
            return resultaat;
        }

    }
}

Foutopvang

Er kunnen zich 2 “run-time” foutmeldingen voordoen bij de invoer.

Indien u geen item uit de keuzelijst hebt geselecteerd krijgt u onderstaande foutmelding “Object reference not set to an instance of an object.”. Vrij vertaald, u hebt geen item geselecteerd.

Indien u eerst een bedrag invoert en dan dat bedrag weer uitwist krijgt u ook een foutmelding “Input string was not in a correct format.”.

U kunt ook de fouten opvangen met een try{} catch{} die we reeds besproken hebben.

Heel de code, hoewel dit in principe te veel is van het goede, komt in de try{}. Een passende foutmelding komt in de catch{}. De fout wordt weergegeven in een boodschappenvenster. Vergeet niet uw functie asynchroon te maken.

De finale code wordt dan:

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

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

        private async void Button_Clicked(object sender, EventArgs e)
        {
            try
            {
                decimal bedrag = 0;
                Int16 percentage = 0;
                decimal btw = 0;

                //invoer
                bedrag = Convert.ToDecimal(entryInvoer.Text);
                percentage = Convert.ToInt16(pickPercentage.SelectedItem.ToString());

                //berekening
                btw = BTW(bedrag, percentage);

                //uitvoer
                lblUitvoer.Text = btw.ToString("F2");
            }
            catch
            {
                //uitvoer
                await DisplayAlert("Foutieve invoer", "U hebt geen correct bedrag of percentage ingevoerd!", "OK");
            }

        }

        public decimal BTW(decimal bedrag, int perc = 21)
        {
            decimal resultaat = bedrag * perc / 100;
            return resultaat;
        }

    }
}

Opdracht

Pas het programmaatje nu nog verder zelf aan zodat u ook het Bedrag met BTW kunt weergeven.


Invoer in detail

Het correcte toetsenbord selecteren

Uw toestel komt met verschillende ingebouwde toetsenborden voor verschillende specifieke invoer. In bovenstaand voorbeeld hebben we daar gebruik van gemaakt om het numerieke toetsenbord te selecteren via de eigenschap Keyboard="Numeric" van de Entry.

<Entry Keyboard="Numeric" ></Entry>

Hier vindt u een overzicht van de beschikbare toetsenborden.

Om dit uit te testen maakt u een nieuw project aan.

XAML

Pas de XAML van de pagina Mainpage.xaml als volgt aan:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Toetsenborden" x:Class="Toetsenborden.MainPage">

    <StackLayout>
        <Entry Placeholder="Default" Keyboard="Default" />
        <Entry Placeholder="Text" Keyboard="Text" />
        <Entry Placeholder="Chat" Keyboard="Chat" />
        <Entry Placeholder="Url" Keyboard="Url" />
        <Entry Placeholder="Email" Keyboard="Email" />
        <Entry Placeholder="Telephone" Keyboard="Telephone" />
        <Entry Placeholder="Numeric" Keyboard="Numeric" />
        <Entry Placeholder="Password" IsPassword="true" />
    </StackLayout>

</ContentPage>

Merk op dat voor de invoer van een wachtwoord geen Keyboard is voorzien maar dat dit gebeurt door het instellen van de eigenschap IsPassword="true".

Events: TextChanged en Completed

Een Entry en een Editor komen met 2 events:

  • TextChanged: deze event wordt uitgevoerd iedere keer de tekst gewijzigd wordt.
  • Completed: deze event wordt uitgevoerd wanneer de gebruiker de tekstinvoer beëindigt.

Om dit uit te testen maakt u een nieuw project aan.

XAML

Pas de XAML van de pagina Mainpage.xaml als volgt aan:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:tekstEvents" x:Class="tekstEvents.MainPage">

    <StackLayout>
        <Entry x:Name="Invoer" TextChanged="Invoer_TextChanged" Completed="Invoer_Completed"></Entry>
        <Label x:Name="Uitvoer"></Label>
    </StackLayout>

</ContentPage>

Code-behind

Als de tekst wijzigt TextChanged moet de gewijzigde tekst meteen getoond worden. Als het invoeren van tekst beëindigd wordt Completed dan krijgt de uitvoer een rode achtergrond.

Pas de code-behind van Mainpage.xaml.cs als volgt aan:

using System;
using Xamarin.Forms;

namespace tekstEvents
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Invoer_TextChanged(object sender, TextChangedEventArgs e)
        {
            Uitvoer.Text = Invoer.Text;
        }

        private void Invoer_Completed(object sender, EventArgs e)
        {
            Uitvoer.BackgroundColor = Color.Red;
        }
    }
}

Binnen de event TextChanged kunt u de oude waarde opvragen via e.OldTextValue en de nieuwe waarde via e.NewTextValue.


Design guides

Onderstaande design guides sluiten aan bij wat besproken is in deze post. Ik raad aan deze zeker eens te bekijken.

Android

iOS

UWP


Behandelde Basiscompetenties uit de module ICT Programmeren – Specifieke ontwikkelomgeving: eenvoudige functionaliteiten

  • IC BC017 – kan ICT veilig en duurzaam gebruiken
  • IC BC234 – kan de basisprincipes van programmeren in een specifieke ontwikkelomgeving toepassen
  • IC BC236 – kan eenvoudige wijzigingen aan een programma aanbrengen
  • IC BC241 – kan een programma in een specifieke ontwikkelomgeving maken
  • IC BC250 – kan bij het programmeren in functie van een specifieke ontwikkelomgeving, een juiste logica volgen

Eén reactie

  1. Om al een waarde mee te geven in de picker kan men de Picker.SelectedIndex na de Picker.ItemsSource meegeven. SelectedIndex 0 is de eerste uit de picker lijst.

Geef een reactie

Deze website gebruikt Akismet om spam te verminderen. Bekijk hoe je reactie-gegevens worden verwerkt.

  • 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.