Xamarin.Forms - Créer un header grâce au ControlTemplate

# .Net# Xamarin.Forms
écrit par Tom Cortinovis publié le mercredi 21 novembre 2018
Dans les applications mobiles, il n’est pas rare que votre graphiste préféré vous fasse intégrer un header qui se répète dans plusieurs pages. Confronté plusieurs fois à cette situation, je vais vous présenter la solution que j’utilise dans ce cas. Elle présente l’avantage d’être simple à mettre en place, ainsi vous respecterez la 1ere des trois vertus du programmeur : la paresse ! Cette méthode se base sur un composant parfois méconnu dans Xamarin.Forms : le ControlTemplate.

De quoi ai-je besoin pour me lancer ?

D’assez peu de choses à vrai dire ! Des connaissances en XAML vous seront bien sûr utiles, que ce soit en Xamarin.Forms ou grâce au WPF. Connaître le pattern MVVM vous aidera également dans la compréhension du dernier exemple de cet article. Si ces deux points vous parlent, vous pouvez vous lancer (et même si ce n’est pas le cas, n’hésitez pas à lire la suite quand même !)

Pourquoi choisir un ControlTemplate pour répondre à cette problématique

Il est vrai que je pourrais créer un header de différentes manières. Par exemple, en utilisant une NavigationPage, Xamarin.Forms ajoute automatiquement un header. Le soucis est que cette barre est très peu personnalisable. Il sera donc rapidement nécessaire d’utiliser des Renderers et donc de maintenir autant de classes que vous supportez de plateformes. Une autre solution, plus “artisanale” serait de créer un composant Header et de le rajouter en haut de chacune des pages où l’on souhaite l’inclure. Une méthode pas très automatisée ! D'après moi, le ControlTemplate est la solution adéquate car il résout les deux soucis évoqués ci-dessus. C'est ce que nous allons maintenant voir.

Un header en 5 minutes chrono

Assez parlé, lançons-nous tout de suite dans notre exemple !
<ResourceDictionary>
    <ControlTemplate
        x:Key="HeaderTemplate">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition
                    Height="60" />
                <RowDefinition
                    Height="*" />
            </Grid.RowDefinitions>
            <Grid
                BackgroundColor="DeepSkyBlue"
                Grid.Row="0">
            </Grid>
            <ContentPresenter
                Grid.Row="1" />
        </Grid>
    </ControlTemplate>
</ResourceDictionary>
<?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:TestHeader"
             x:Class="TestNavBar.MainPage"
             BackgroundColor="LightGray"
	Title=”Page 1”
             ControlTemplate="{StaticResource HeaderTemplate}">

	<Label Text="Ecran principal" 
           VerticalOptions="Center" 
           HorizontalOptions="Center" />

</ContentPage>
Comme l’exemple de cette étape le montre, il y a deux parties distinctes. En premier lieu, on place notre ControlTemplate dans l’App.xaml. De cette manière, on rend cette ressource disponible pour toute l’application. Les différentes pages souhaitant utiliser ce template pourront ainsi le faire facilement en se servant de la clé que l’on a donné au ControlTemplate. Ensuite, on ajoute un peu de layout pour placer le header en haut de la page. L’élément ContentPresenter sert ici de « placeholder » pour indiquer où va se positionner le contenu de l’élément qui se sert du ControlTemplate. Maintenant que notre template est créé, ne reste plus qu’à l’utiliser. Et la bonne nouvelle c’est que cela est très simple ! Il suffit d’utiliser la propriété ControlTemplate de notre ContentPage et de lui donner notre template en se servant de la clé que l’on lui a donné précédemment et le tour est joué. Attention toutefois, n’importe quel composant ne peut pas utiliser un ControlTemplate. Un petit voyage dans la documentation de Xamarin.Forms nous apprend que cette propriété est définie dans les classes TemplatedView et TemplatedPage. Cela tombe bien, ContentPage hérite directement de TemplatedPage et c’est pour cette raison que nous avons pu lui donner notre template. Si vous souhaitez utiliser un control que vous avez créé, n’oubliez pas de le faire hériter d’une de ces classes :
  • TemplatedView
  • TemplatedPage
  • ContentView
  • ContentPage
Voici le résultat : En quelques lignes de code nous avons déjà pu créer une première version de notre header. C’est plutôt prometteur… mais pour le moment il est un peu vide. Pour le remplir un peu, voyons comment faire pour binder des informations dans le ControlTemplate.
<Grid
    BackgroundColor="DeepSkyBlue"
    Grid.Row="0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition
            Width="0.5*" />
        <ColumnDefinition
            Width="0.5*" />
    </Grid.ColumnDefinitions>
    <Label
        Text="{TemplateBinding Title}"
        FontSize="Large"
        Grid.Column="0">
    </Label>
    <StackLayout
        Grid.Column="1"
        Orientation="Horizontal">
        <Label
            Text="Utilisateur actif :" />
        <Label
            Text="{TemplateBinding BindingContext.UserName}" />
    </StackLayout>
</Grid>
public class MainPageViewModel
{
    public string UserName => "Toto";
}
Notre ControlTemplate s’est un peu étoffé. Nous avons ajouté un titre et le nom de l’utilisateur courant dans deux colonnes du header. Plutôt qu'un Binding « simple », j'utilise ici « TemplateBinding ». C’est un binding un peu spécial qui est utilisé spécifiquement dans le cas d’un ControlTemplate pour dire que la source de données est le parent qui utilise le template. Dans le 1er binding, je peux donc récupérer directement la propriété Title de la ContentPage grâce à {TemplateBinding Title}. Pour utiliser une propriété du ViewModel, c’est à peine plus long, il faut juste binder sur la propriété BindingContext pour avec accès au VM puis ensuite choisir la propriété qui nous intéresse. Et voilà le résultat final : La promesse est tenue, j'ai pu créér un header réutilisable dans n’importe quelle page et cela en 1 seule ligne ! Mais pouvons-nous utiliser le ControlTemplate dans d’autres scénarios ?

Allons plus loin

En effet, il est très facile d’imaginer comment nous pouvons améliorer notre exemple. En ajoutant une flèche et un bouton "accueil" voilà le simple header transformé en barre de navigation. Une autre utilisation plus avancée du ControlTemplate permet de personnaliser l’apparence d’un composant. L’idée générale est décrite dans cet article. Même si cet exemple est en WPF, l’idée générale peut être transposée en Xamarin.Forms avec un peu de travail. Alors, à vos claviers !