Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Xamarin.Forms et les Control Templates

Introduit depuis la version 2.1.0 les Control Templates sont un ajout essentiel pour structurer le code d’UI. Souvent mal compris, voire ignorés, il est temps de leur rendre justice !

Trop abstrait ?

Xamarin.Forms 2.1.0 a introduit les Control Templates. Pour les non-initiés ou non avertis disons que la gestion des modèles de contrôle est un mécanisme qui permet de séparer la hiérarchie de la vue logique de la hiérarchie visuelle. Une autre façon de penser la chose est qu’un modèle produit la hiérarchie visuelle de votre contrôle ou de votre page. Le concept peut être déroutant au début, mais il est extrêmement puissant une fois compris. Mais pour comprendre il faut voir l’intérêt et pour voir l’intérêt il faut comprendre un minimum… La boucle est bouclée et je pense que c’est pour cela que le Control Templating est l’une des notions les moins connues en pratique, en tout cas pour tous ceux qui ne viennent pas de WPF ou Silverlight où ces concepts étaient primordiaux.

Prenons un exemple simple

Pour créer un modèle de contrôle, nous avons d'abord besoin d'une vue qui peut être modélisée (qui supporte le templating). Xamarin.Forms fournit actuellement des propriétés ControlTemplate sur les types suivants :

  • ContentPage
  • ContentView
  • TemplatedPage
  • TemplatedView

Les différences entre chacun de ces types sont sans importance à ce stade, nous allons donc travailler avec le plus simple, une TemplatedPage.

    public partial class LoginPage : TemplatedPage
    {
        public LoginPage ()
        {
            InitializeComponent ();

            BindingContext = new LoginPageViewModel ();
        }
    }

Et nous allons continuer en appliquant un style que nous récupérons dans notre dictionnaire de ressources d'application

<?xml version="1.0" encoding="utf-8" ?>
<TemplatedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TemplatingDemo.Views.LoginPage"
             Title="{Binding Title}"
             Style="{StaticResource LoginPageStyle }">
</TemplatedPage>

LoginPageStyle est défini dans le dictionnaire de ressources App.xaml :

<ControlTemplate x:Key="LoginTemplate">
  <StackLayout VerticalOptions="CenterAndExpand" Spacing="20" Padding="20">
    <Entry Text="{TemplateBinding Username}" Placeholder="Username" />
    <Entry Text="{TemplateBinding Password}" Placeholder="Password" />
    <Button Command="{TemplateBinding Command}" Text="Click Here To Log In" />
  </StackLayout>
</ControlTemplate>

<Style TargetType="views:LoginPage" x:Key="LoginPageStyle">
  <Style.Setters>
    <Setter Property="ControlTemplate" Value="{StaticResource LoginTemplate}" />
  </Style.Setters>
</Style>

Nous avons maintenant suffisamment de matière pour comprendre ce qui se passe ici. La TemplatedPage a une propriété ControlTemplate qui est définie via un style nommé LoginPageStyle. À l'intérieur du ControlTemplate, il existe des éléments utilisant une nouvelle syntaxe appelée TemplateBinding.

Regardons d’un peu plus près l’une des lignes du template qui contient un champ Entry avec un binding sur sa propriété Text :

<Entry Text="{TemplateBinding Username}" Placeholder="Username" />

Cela devrait ressembler à un binding habituel car ils fonctionnent presque de manière identique, mais la source de la liaison d’un modèle (template) est toujours définie comme étant le parent du modèle, qui dans ce cas est la LoginPage. Alors à quoi est-ce bindé ? Bonne question, en fait du code a été omis. LoginPage expose les propriétés pouvant être liées au modèle (template).

    public partial class LoginPage : TemplatedPage
    {
        public LoginPage ()
        {
            // - - -
        }

        public static readonly BindableProperty UsernameProperty =
            BindableProperty.Create ("Username", typeof (string), typeof (LoginPage), null, BindingMode.TwoWay);

        public string Username
        {
            get { return (string)GetValue (UsernameProperty); }
            set { SetValue (UsernameProperty, value); }
        }

        // autres propriétés omises pour simplifier l’exemple
    }

A la fin nous obtenons une page qui ressemble à cela :

image

En effet, les vues à l'intérieur du ControlTemplate sont liées à des valeurs de LoginPage qui sont à leur tour liées au ViewModel de la manière normale tel que vous le feriez habituellement en MVVM. Cela peut sembler un peu lourd pour cet exemple, et pour être honnête, c'est le cas. Mais un exemple est toujours réducteur, jusqu’au point où il perd forcément de sa force démonstratrice…mais auriez vu lu l’article si les bouts de code avaient fait 15 pages… pas sûr !

Pourquoi cette complexité ?

Il existe plusieurs scénarios où cette technique vous permet de faire des choses qui étaient auparavant assez difficiles. Surtout, il s'agit de thématiser et de rendre les choses belles. Avec les pages disposant d’une propriété ControlTemplates nourrie avec un contenu standardisé, votre application peut être facilement thématisée et rethématisée au moment de l'exécution pour avoir des looks différents tout en présentant exactement les mêmes informations. Il suffit d'appliquer un style différent avec un ControlTemplate différent et tout devient différent sans vraiment changer votre code !

Il est bon de noter aussi que bien que cet exemple ait examiné TemplatedPage, les TemplatedView / ContentView offrent beaucoup plus de flexibilité et de “composabilité” car ce sont des vues et non des pages en taille réelle. Cela signifie qu'il est beaucoup plus probable de les voir présenter des données similaires / identiques mais avec des vues différentes. On retrouve là l’esprit de la composition des pages par région qu’on avait dans WPF par exemple.

A quoi servent TemplatedView / Page?

TemplatedPage sert de classe de base, remplaçant ContentPage comme Page la plus basique. Contrairement à ContentPage, TemplatedPage n'a pas de propriété Content. Par conséquent, vous ne pouvez pas y insérer directement de contenu. Cela signifie que la seule façon d'obtenir du contenu dans une TemplatedPage est de définir le ControlTemplate, sinon il apparaîtra vide. La même chose n'est pas vraie d'un ContentPage, où la propriété Content peut être affectée ainsi que la définition d'un ControlTemplate.

C'est quand on affecte les deux que les choses commencent à devenir vraiment intéressantes. Si le ControlTemplate a été modifié pour ressembler à :

<ControlTemplate x:Key="LoginTemplate">
  <StackLayout VerticalOptions="CenterAndExpand" Spacing="20" Padding="20">
    <Entry Text="{TemplateBinding Username}" Placeholder="Username" />
    <Entry Text="{TemplateBinding Password}" Placeholder="Password" />
    <Button Command="{TemplateBinding Command}" Text="Click Here To Log In" />
    <ContentPresenter />
  </StackLayout>
</ControlTemplate>

Et en l’appliquant plutôt à ContentPage, the ContentPage.Content se retrouvera à l’intérieur du ContentPresenter dans le ControlTemplate. Le ControlTemplate servira alors de couche intermédiaire pour le ContentPage et son Content !

Est-ce conforme à MVVM ?

Oui bien entendu, mais l’exemple hyper réduit ici ne rend pas grâce à cet aspect et peut même le rendre un peu confus (surtout avec des propriétés exposées aussi dans la page). Mais le but du présent papier est d’en “remettre une couche” sur le Templating en tentant de l’expliquer autrement et en se focalisant juste sur ce concept.

En “remettre une couche” ? Y-aurait-il une première couche que vous auriez loupée ?

Humm c’est possible !

En effet j’ai déjà produit une vidéo de près de 52 minutes sur le sujet en mai 2019 … Je ne vous en veux pas de l’avoir loupée, mais maintenant c’est peut-être le bon moment pour y aller non ? Smile

Suivez le lien : https://www.youtube.com/watch?time_continue=4&v=T2dTlRJni9E

En presque une heure et en voyant ce qui se passe étape par étape cela sera certainement plus facile à comprendre avec un exemple qui tourne en vrai sous vos yeux.


Aller plus loin

Je pense que je vais m’arrêter ici… Trop long serait contreproductif. L’approche est très nouvelle pour certain et cela fait déjà beaucoup d’information à métaboliser ! Vouloir en rajouter pour le plaisir d’être plus “complet” n’aurait pour effet que de vous noyer.

Mais je vous enjoins à aller plus loin bien entendu !

Mais la seule façon de vraiment comprendre ces concepts reste de les pratiquer. Réservez-vous un peu de temps, quelques heures d’affilée, au moins deux. Et tester cette histoire de templating avec vos petits doigts sur le clavier ! Partez d’un projet vierge, n’essayez pas de faire quelque chose de beau, essayez de vous concentrer sur le principe même.

Et à un moment vous comprendrez toute la puissance du concept… et vous saurez l’utiliser quand cela sera nécessaire !

Rendez-vous aussi sur cet autre article qui traite du même sujet autrement, plusieurs angles de vue finissent par avoir raison des concepts parfois trop abstraits au premier abord ! Le Control Templating sous Xamarin.Forms (Dot.Vlog N°19)

Conclusion

Il y a beaucoup d'autres choses intéressantes à faire avec les ControlTemplates, et je suis sûr que beaucoup d’entre vous finiront par trouver des idées qui ne me viennent pas en tête à l’instant.

Le plus important ici est de pratiquer au moins deux heures pour être sûr d’avoir compris comment tout cela fonctionne. Et si vous n’avez pas d’idées pour l’instant, ne vous inquiétez pas elles finiront par venir en même temps que la compréhension du Templating !

Et ne loupez pas la vidéo une seconde fois Smile

Stay Tuned !

blog comments powered by Disqus