Mobiele apps programmeren met Xamarin – XAML optimalisatie volgens het DRY-principe – Templates – ControlTemplate

print

Inhoud


Wat vooraf ging


Wat is een template?

Een template gaat een stapje verder dan een stijl. Waar een stijl een verzameling is van eigenschappen die een specifieke waarde hebben, is een template een voorgemaakte opmaak, inclusief objecten/controls.

Een template (ook wel een sjabloon genoemd) is dus basisontwerp waar u kunt op verder bouwen. Hier vindt u alvast een paar voorbeelden van templates die u gratis kunt downloaden.

Xamarin kent 2 soorten templates:

  • ControlTemplates – worden gebruikt als basisontwerp voor pagina’s (vergelijk het met een thema).
  • DataTemplates – worden gebruikt om data/gegevens om een gewenste manier weer te geven.

Hier bespreken we de ControlTemplate. De DataTemplate komt later aan bod.

Wat is een ControlTemplate?

Een ControlTemplate bepaalt het uitzicht van een Page of een View en bevat de “root layout” (basislayout) waarbinnen zich de controls bevinden behorende tot de template.

Een Controltemplate bevat een ControlPresenter die dienst doet als een soort “placeholder” waar de pagina of view typische inhoud wordt weergegeven.

Hieronder ziet u een voorbeeld van een ControlTemplate.

Een ControlTemplate kan worden toegepast op een:


Waar plaatst u de ControlTemplate?

Omdat templates “per definitie” voor heel het project, op alle pagina’s worden toegepast worden ze bij voorkeur geplaatst in de Application ResourceDictionary in het bestand App.xaml.

<?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="TestTemplates.App">
	<Application.Resources>

		<!-- Application resource dictionary -->

	</Application.Resources>
</Application>

Binnen deze Application.Resources plaatst u de ControlTemplate die u tenvens een naam geeft via x:Key="TealTemplate".

<?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="TestTemplates.App">
    <Application.Resources>
        <ResourceDictionary>
             <ControlTemplate x:Key="TealTemplate">

            </ControlTemplate> 
            
        </ResourceDictionary>

    </Application.Resources>
</Application>

Vervolgens voegen we de XAML toe om onderstaande template aan te maken:

<?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="TestTemplates.App">
	<Application.Resources>
        <ResourceDictionary>
             <ControlTemplate x:Key="TealTemplate">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="0.1*" />
                        <RowDefinition Height="0.8*" />
                        <RowDefinition Height="0.1*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*" />
                        <ColumnDefinition Width="0.95*" />
                    </Grid.ColumnDefinitions>

                    <BoxView Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Column="1" Text="Control Template Demo App" TextColor="White" VerticalOptions="Center" />
                

                
                    <BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Row="2" Grid.Column="1" Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" />
                </Grid>

            </ControlTemplate> 
                               
        </ResourceDictionary>

    </Application.Resources>
</Application>

Wat we tot nu toe ontworpen hebben is de “omkadering” waarbinnen de pagina’s zullen worden getoond. De plek waar deze pagina’s zullen worden getoond dient aangeduide met de ControlPresenter.

<ContentPresenter />
<?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="TestTemplates.App">
	<Application.Resources>
        <ResourceDictionary>
             <ControlTemplate x:Key="TealTemplate">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="0.1*" />
                        <RowDefinition Height="0.8*" />
                        <RowDefinition Height="0.1*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*" />
                        <ColumnDefinition Width="0.95*" />
                    </Grid.ColumnDefinitions>

                    <BoxView Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Column="1" Text="Control Template Demo App" TextColor="White" VerticalOptions="Center" />
                
                    <ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
                
                    <BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Row="2" Grid.Column="1" Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" />
                </Grid>

            </ControlTemplate> 
                               
        </ResourceDictionary>

    </Application.Resources>
</Application>
  • Voeg nog een tweede ControlTemplate toe die dezelfde inhoud heeft maar een andere kleur en geef hetm de naam x:Key="AquaTemplate".
<?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="TestTemplates.App">
	<Application.Resources>
        <ResourceDictionary>
             <ControlTemplate x:Key="TealTemplate">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="0.1*" />
                        <RowDefinition Height="0.8*" />
                        <RowDefinition Height="0.1*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*" />
                        <ColumnDefinition Width="0.95*" />
                    </Grid.ColumnDefinitions>

                    <BoxView Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Column="1" Text="Control Template Demo App" TextColor="White" VerticalOptions="Center" />
                
                    <ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
                
                    <BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Row="2" Grid.Column="1" Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" />
                </Grid>

            </ControlTemplate> 
            
             <ControlTemplate x:Key="AquaTemplate">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="0.1*" />
                        <RowDefinition Height="0.8*" />
                        <RowDefinition Height="0.1*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*" />
                        <ColumnDefinition Width="0.95*" />
                    </Grid.ColumnDefinitions>
                    <BoxView Grid.ColumnSpan="2" Color="Aqua" />
                    <Label Grid.Column="1" Text="Control Template Demo App" TextColor="White" VerticalOptions="Center" />
                    <ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
                    <BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Aqua" />
                    <Label Grid.Row="2" Grid.Column="1" Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" />
                </Grid>
            </ControlTemplate>           
            
        </ResourceDictionary>

    </Application.Resources>
</Application>

Een template toepassen op een pagina

Nu de ControlTemplate aangemaakt is moet deze ook nog toegepast worden op de gewenste pagina.

  • Ga naar MainPage.xaml.
  • Maak onderstaande eenvoudige pagina aan met een Label en een Button met een Clicked-event.
<?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:TestTemplates" x:Class="TestTemplates.MainPage">

    <StackLayout Margin="20">
 	<Label Text="Welkom op het PCVO Dender en Schelde!" VerticalOptions="Center" HorizontalOptions="Center" />
        <Button Text="Wijzig het thema" Clicked="Button_Clicked"></Button>
    </StackLayout>

</ContentPage>

De ControlTemplate wordt gebonden aan de eigenschap ControlTemplate="{StaticResource TealTemplate} van de ContentPage.

<?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:TestTemplates" x:Class="TestTemplates.MainPage" ControlTemplate="{StaticResource TealTemplate}">

    <StackLayout Margin="20">
 	<Label Text="Welkom op het PCVO Dender en Schelde!" VerticalOptions="Center" HorizontalOptions="Center" />
        <Button Text="Wijzig het thema" Clicked="Button_Clicked"></Button>
    </StackLayout>

</ContentPage>

Bij het klikken op de Button moet de andere ControlTemplate geladen worden. Dit gebeurt via C# in code-behind.

Gebruik een variabele om na te gaan welke van de 2 templates momenteel geladen is.

bool originalTemplate = true;

Maak variabelen om de ControlTemplate bij te houden.

ControlTemplate tealTemplate;
ControlTemplate aquaTemplate;

Ken na de initialisatie de ControlTemplate toe.

tealTemplate = (ControlTemplate)Application.Current.Resources["TealTemplate"];
aquaTemplate = (ControlTemplate)Application.Current.Resources["AquaTemplate"];

Bij het klikken op de Button wordt de niet geladen ControlTemplate geladen.

originalTemplate = !originalTemplate;
ControlTemplate = (originalTemplate) ? tealTemplate : aquaTemplate;

De volledige code wordt dus:

using System;
using Xamarin.Forms;

namespace TestTemplates
{
    public partial class MainPage : ContentPage
    {
        bool originalTemplate = true;
        ControlTemplate tealTemplate;
        ControlTemplate aquaTemplate;

        public MainPage()
        {
            InitializeComponent();
            tealTemplate = (ControlTemplate)Application.Current.Resources["TealTemplate"];
            aquaTemplate = (ControlTemplate)Application.Current.Resources["AquaTemplate"];

        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            originalTemplate = !originalTemplate;
            ControlTemplate = (originalTemplate) ? tealTemplate : aquaTemplate;
        }
    }
}

TemplateBinding

Stel dat u binnen de ControlTemplate toch de vrijheid wilt om bv. op iedere pagina een andere titel weer te kunnen geven. Dit kan door gebruik te maken van TemplateBinding (TemplateBinding kan enkel binnen een ControlTemplate).

Eerst moet de Text binnen de ControlTemplate de Labels gewijzigd worden naar een TemplateBinding.

Text="{TemplateBinding HeaderText}" 
Text="{TemplateBinding FooterText}"  

De volledige XAML is nu:

<?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="TestTemplates.App">
	<Application.Resources>
        <ResourceDictionary>
             <ControlTemplate x:Key="TealTemplate">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="0.1*" />
                        <RowDefinition Height="0.8*" />
                        <RowDefinition Height="0.1*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*" />
                        <ColumnDefinition Width="0.95*" />
                    </Grid.ColumnDefinitions>

                    <BoxView Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Column="1" Text="{TemplateBinding HeaderText}" TextColor="White" VerticalOptions="Center" />
                
                    <ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
                
                    <BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Teal" />
                    <Label Grid.Row="2" Grid.Column="1" Text="{TemplateBinding FooterText}" TextColor="White" VerticalOptions="Center" />
                </Grid>

            </ControlTemplate> 
            
             <ControlTemplate x:Key="AquaTemplate">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="0.1*" />
                        <RowDefinition Height="0.8*" />
                        <RowDefinition Height="0.1*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="0.05*" />
                        <ColumnDefinition Width="0.95*" />
                    </Grid.ColumnDefinitions>
                    <BoxView Grid.ColumnSpan="2" Color="Aqua" />
                    <Label Grid.Column="1" Text="{TemplateBinding HeaderText}" TextColor="White" VerticalOptions="Center" />
                    <ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
                    <BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Aqua" />
                    <Label Grid.Row="2" Grid.Column="1" Text="{TemplateBinding FooterText}" TextColor="White" VerticalOptions="Center" />
                </Grid>
            </ControlTemplate>           
            
        </ResourceDictionary>

    </Application.Resources>
</Application>

Vervolgens moet HeaderText en FooterText aangemaakt worden als BindableProperty en toegankelijk worden gemaakt.

Aanmaken als BindableProperty

Een BindableProperty moet gecreëerd worden. Dit gebeurt via:

public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null);

Of concreet:

public static readonly BindableProperty HeaderTextProperty = BindableProperty.Create("HeaderText", typeof(string), typeof(MainPage), "PCVO Dender en Schelde");
public static readonly BindableProperty FooterTextProperty = BindableProperty.Create("FooterText", typeof(string), typeof(MainPage), "(c) 2017");
  • De 1ste parameter is de specifiek naam van de property.
  • De 2de parameter is het type van de property: typeof(string).
  • De 3de parameter is het type van het object dat de ControlTempate bevat: typeof(MainPage).
  • De 4de parameter bevat de waarde die aan de property wordt toegewezen.

De BindableProperty toegankelijk maken

De BindableProperty toegankelijk maken gebeurt via:

public string EventName
{
    get { return (string)GetValue(EventNameProperty); }
    set { SetValue(EventNameProperty, value); }
}

Aangezien we enkel de waarden moeten kunnen lezen/opvragen get { return (string)GetValue(EventNameProperty); } wordt dit concreet:

public string HeaderText
{
    get { return (string)GetValue(HeaderTextProperty); }
}

public string FooterText
{
    get { return (string)GetValue(FooterTextProperty); }
}

De volledige C# is nu:

using System;
using Xamarin.Forms;

namespace TestTemplates
{
    public partial class MainPage : ContentPage
    {
        bool originalTemplate = true;
        ControlTemplate tealTemplate;
        ControlTemplate aquaTemplate;

        public static readonly BindableProperty HeaderTextProperty = BindableProperty.Create("HeaderText", typeof(string), typeof(MainPage), "PCVO Dender en Schelde");
        public static readonly BindableProperty FooterTextProperty = BindableProperty.Create("FooterText", typeof(string), typeof(MainPage), "(c) 2017");

        public string HeaderText
        {
            get { return (string)GetValue(HeaderTextProperty); }
        }

        public string FooterText
        {
            get { return (string)GetValue(FooterTextProperty); }
        }


        public MainPage()
        {
            InitializeComponent();
            tealTemplate = (ControlTemplate)Application.Current.Resources["TealTemplate"];
            aquaTemplate = (ControlTemplate)Application.Current.Resources["AquaTemplate"];

        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            originalTemplate = !originalTemplate;
            ControlTemplate = (originalTemplate) ? tealTemplate : aquaTemplate;

        }
    }
}


ControleTemplate in C#

Als u ControlTemplates wilt aanmaken via C#-code doet u dat binnen een klasse die afgeleid wordt van de Lay-out waarbinnen de ControlTemplateControlTemplate zich bevindt.

class TealTemplate : Grid
{
  public TealTemplate ()
  {
    ...
    var contentPresenter = new ContentPresenter ();
    ...
  }
}

Hieronder vindt u de volledige C#-code van het bovenstaande voorbeeld:

using Xamarin.Forms;

namespace SimpleTheme
{
	public class HomePageCS : ContentPage
	{
		class TealTemplate : Grid
		{
			public TealTemplate ()
			{
				RowDefinitions.Add (new RowDefinition { Height = new GridLength (0.1, GridUnitType.Star) });
				RowDefinitions.Add (new RowDefinition { Height = new GridLength (0.8, GridUnitType.Star) });
				RowDefinitions.Add (new RowDefinition { Height = new GridLength (0.1, GridUnitType.Star) });
				ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (0.05, GridUnitType.Star) });
				ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (0.95, GridUnitType.Star) });

				var topBoxView = new BoxView { Color = Color.Teal };
				Children.Add (topBoxView, 0, 0);
				Grid.SetColumnSpan (topBoxView, 2);

				var topLabel = new Label {
					Text = "Control Template Demo App",
					TextColor = Color.White,
					VerticalOptions = LayoutOptions.Center
				};
                topLabel.SetBinding(Label.TextProperty, new TemplateBinding("HeaderText"));
                Children.Add (topLabel, 1, 0);

				var contentPresenter = new ContentPresenter ();
				Children.Add (contentPresenter, 0, 1);
				Grid.SetColumnSpan (contentPresenter, 2);

				var bottomBoxView = new BoxView { Color = Color.Teal };
				Children.Add (bottomBoxView, 0, 2);
				Grid.SetColumnSpan (bottomBoxView, 2);

				var bottomLabel = new Label {
					Text = "(c) Xamarin 2016",
					TextColor = Color.White,
					VerticalOptions = LayoutOptions.Center
				};
                bottomLabel.SetBinding(Label.TextProperty, new TemplateBinding("FooterText"));

                Children.Add (bottomLabel, 1, 2);
			}
		}

		class AquaTemplate : Grid
		{
			public AquaTemplate ()
			{
				RowDefinitions.Add (new RowDefinition { Height = new GridLength (0.1, GridUnitType.Star) });
				RowDefinitions.Add (new RowDefinition { Height = new GridLength (0.8, GridUnitType.Star) });
				RowDefinitions.Add (new RowDefinition { Height = new GridLength (0.1, GridUnitType.Star) });
				ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (0.05, GridUnitType.Star) });
				ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (0.95, GridUnitType.Star) });

				var topBoxView = new BoxView { Color = Color.Aqua };
				Children.Add (topBoxView, 0, 0);
				Grid.SetColumnSpan (topBoxView, 2);

				var topLabel = new Label {
					Text = "Control Template Demo App",
					TextColor = Color.Blue,
					VerticalOptions = LayoutOptions.Center
				};
                topLabel.SetBinding(Label.TextProperty, new TemplateBinding("HeaderText"));

                Children.Add (topLabel, 1, 0);

				var contentPresenter = new ContentPresenter ();
				Children.Add (contentPresenter, 0, 1);
				Grid.SetColumnSpan (contentPresenter, 2);

				var bottomBoxView = new BoxView { Color = Color.Aqua };
				Children.Add (bottomBoxView, 0, 2);
				Grid.SetColumnSpan (bottomBoxView, 2);

				var bottomLabel = new Label {
					Text = "(c) Xamarin 2016",
					TextColor = Color.Blue,
					VerticalOptions = LayoutOptions.Center
				};
                bottomLabel.SetBinding(Label.TextProperty, new TemplateBinding("FooterText"));

                Children.Add (bottomLabel, 1, 2);
			}
		}

		bool originalTemplate = true;
		ControlTemplate tealTemplate = new ControlTemplate (typeof(TealTemplate));
		ControlTemplate aquaTemplate = new ControlTemplate (typeof(AquaTemplate));

        public static readonly BindableProperty HeaderTextProperty = BindableProperty.Create("HeaderText", typeof(string), typeof(HomePageCS), "PCVO Dender en Schelde");
        public static readonly BindableProperty FooterTextProperty = BindableProperty.Create("FooterText", typeof(string), typeof(HomePageCS), "(c) 2017");

        public string HeaderText
        {
            get { return (string)GetValue(HeaderTextProperty); }
        }

        public string FooterText
        {
            get { return (string)GetValue(FooterTextProperty); }
        }


        public HomePageCS ()
		{

			var button = new Button { Text = "Wijzig het thema" };
            button.Clicked += (sender, e) => {
				originalTemplate = !originalTemplate;
				ControlTemplate = (originalTemplate) ? tealTemplate : aquaTemplate;
			};

            Padding = new Thickness(0, 20, 0, 0);
			Content = new StackLayout
            {
                VerticalOptions = LayoutOptions.CenterAndExpand,
                Children = {
                    new Label { Text = "Welkom op het PCVO Dender en Schelde!", HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center },
                    button
                }
            };

			ControlTemplate = tealTemplate;

		}
	}
}


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

  • IC BC253 – kan broncode in een specifieke ontwikkelomgeving optimaliseren

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.