Créer un contrôle réutilisable 100% Xamarin.Forms, partie 2
Tags: xamarinforms, xaml, mvvm, french
Published: 2020-11-12 14:46:52

---
title: Créer un contrôle réutilisable 100% Xamarin.Forms, partie 2
published: true
date: 2020-11-10 17:22:28 UTC
tags: XamarinForms,XAML,MVVM,french
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/73h9pysohbu0a9auxyqf.jpg
canonical_url: https://www.sylvainmoingeon.fr/creer-un-controle-reutilisable-100-xamarinforms-partie-2/
---

Vous apprendrez notamment à créer des propriétés bindables (`BindableProperty`) et à utiliser les `Converters`. 

Si ce n'est pas déjà fait, je vous invite à d'abord consulter la [première partie de cette article](https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-1-4mgo) dans laquelle nous avons construit les bases d'une image circulaires avec quelques options (placeholder, bordure...).

* * *

## Mais où va-t-on ? 

Commençons par un petit rappel, voici une démonstration animée de l'image circulaire en situation.

> Comme je suis un gars vraiment sympa, pour le même prix je vous ajoute une copie d'écran iOS. La base de code est 100% commune, **un seul code pour générer deux applications natives**. C'est la beauté de Xamarin.Forms.

 On y voit : 

- Des images circulaires
- Une bordure s'afficher si le contact est placé dans les favoris
- Un indicateur de chargement tourbillonner brièvement sur les deux dernières images
- Des images de substitution par défaut s'il n'existe pas d'image valide (image non renseignée, url invalide ou inaccessible...)

[![Screenshot animé sous Android](https://www.sylvainmoingeon.fr/media/posts/37/gallery/demo-circleimage.gif)](https://www.sylvainmoingeon.fr/media/posts/37/gallery/demo-circleimage.gif)<figcaption>L'image circulaire en situation sous Android</figcaption>

[![Screenshot sous iOS](https://www.sylvainmoingeon.fr/media/posts/37/gallery/circleimage-ios-screenshot-thumbnail.png)](https://www.sylvainmoingeon.fr/media/posts/37/gallery/circleimage-ios-screenshot.png)<figcaption>Copie d'écran sous iOS</figcaption>

La mise-en-page est très simple, un titre, un sous-titre indiquant le nombre de favoris et une liste de contacts.

Par contre, hors de question de reprendre tel quel le XAML de l'image circulaire créée dans l'article précédent et de le coller comme ça, l'air de rien. Hé ! Oh ! On est des vrais professionnels n'est-ce pas ? Nous allons donc en faire un contrôle découplé du reste du code et réutilisable au besoin partout dans le projet.

## Création du contrôle 

### Composons 

Souvenez-vous, dans la première partie de l'article nous avions créé une [image circulaire](https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-1-4mgo) à l'aide de la propriété `Clip` et d'une `EllipseGemotry`. Puis nous l'avions agrémentée de quelques options en y superposant d'autres contrôles dans une `Grid`.

Nous avons donc une base avec une image circulaire simple : 

```xml
<Image Source="monimage.png" WidthRequest="128" HeightRequest="128">
    <Image.Clip>
        <EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64" />
    </Image.Clip>
</Image>
```

Et une image circulaire avancée, composée à partir de la précédente :

```xml
<Grid>
    <Image Source="monPlaceholder.png" WidthRequest="128" HeightRequest="128">
        <Image.Clip>
            <EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64" />
        </Image.Clip>
    </Image>

    <Image Source="monimage.png" WidthRequest="128" HeightRequest="128">
        <Image.Clip>
            <EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64" />
        </Image.Clip>
    </Image>

    <Ellipse Margin="0"
             HorizontalOptions="Center"
             VerticalOptions="Center"
             Stroke="Yellow"
             StrokeThickness="2"
             HeightRequest="128"
             WidthRequest="128"
             />

     <ActivityIndicator IsRunning="{Binding Source={x:Reference monImage}, Path=IsLoading}"
                        VerticalOptions="Center"
                        HorizontalOptions="Center"
                        />
</Grid>
```

Nous avons le squelette de notre contrôle mais quelque chose me chiffonne. Ce n'est tout de même pas bien beau cette duplication de code. Et si, au lieu de copier-coller le code de l'image "clippée" avec l'ellipse, on en faisait un contrôle d'image circulaire tout simple mais réutilisable ?

Notre image étant inscrite dans un cercle, cela me gêne de définir `HeightRequest` et `WidthRequest`. Les deux propriétés auront toujours la même valeur, autant définir une nouvelle propriété `ImageSize`.

Quelque chose comme ça :

```xml
<Grid>
    <local:CircleImage x:Name="monPlaceholder"
                         Source="monPlaceholder.png"
                         ImageSize="128" />
                         
    <local:CircleImage x:Name="monImage"
                         Source="monImage.png"
                         ImageSize="128" />

    [...] Le reste du contrôle avec la bordure et l'activityIndicator
</Grid>
```

Nous allons donc commencer par créer un contrôle `CircleImage` qui hérite du contrôle Image de base, en y ajoutant une propriété `ImageSize` et un "clipping" circulaire.

Et comme affecter des valeurs en dur ça n'a pas beaucoup d'intérêt dans un vrai projet, nous rendrons la propriété `ImageSize` bindable de façon à pouvoir écrire quelque chose comme :

```xml
<local:CircleImage x:Name="monPlaceholder"
                     Source="{Binding MonImage}"
                     ImageSize="{Binding MyImageSize}" />
```

La propriété `Source` héritant du contrôle Image de base, elle est déjà bindable.

Si ceci vous parait obscur, relisez mon article sur l'[architecture MVVM](https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g) !

### Créer un nouveau contrôle 

Pour faire les choses proprement, créez un dossier _CircleImage_ à la base de votre projet Xamarin.Forms et ajoutez-y un `ContentView` nommé CircleImage. Il n'y a pas de mal à être pragmatique sur le nommage. 😁

![Copie d'écran pour illustrer l'ajout d'un ContentView dans Xamarin.Forms](https://www.sylvainmoingeon.fr/media/posts/37/add-content-view.png)<figcaption>Ajoutez un ContentView nommé CircleImage</figcaption>

Par défaut, votre `ContentView` est de type... `ContentView`. Ça va de soi mais je le précise pour les deux qui dorment au fond vers le radiateur.

Nous pourrions ajouter notre `Image` comme contenu du `ContentView`, mais pourquoi alourdir le code et l'affichage natif en imbriquant des contrôles alors que nous avons uniquement besoin d'une image ? Nous allons donc remplacer le type `ContentView` par le type `Image`.

Attention ! Pensez bien à le faire à la fois dans le XAML...

```xml
<!-- Remplacer -->
<ContentView x:Class="CircleImageDemo.CircleImage.CircleImage"
<-- Par -->
<Image x:Class="CircleImageDemo.CircleImage.CircleImage"
```

...et dans le code-behind C# !

```csharp
// Remplacer
public partial class CircleImage : ContentView { }
// Par
public partial class CircleImage : Image { }
```

Nous avons presque tout ce qu'il nous faut, il s'agit juste de créer le découpage circulaire dans le XAML et d'ajouter la propriété bindable `ImageSize`. Commençons par ceci.

### Exposer des propriétés bindables depuis l'extérieur 

Je suis en train de créer un contrôle visuel et je souhaite exposer une propriété qui soit bindable depuis et/ou vers l'extérieur. Il s'agit principalement de définir une propriété qui encapsule une `BindableProperty` au lieu d'un champs privé comme cela se fait habituellement.

Je vous vois froncer les sourcils, alors un exemple : 

```csharp
#region ImageSize Bindable property
// Bindable property
public static readonly BindableProperty ImageSizeProperty =
  BindableProperty.Create(
     propertyName: nameof(ImageSize),
     declaringType: typeof(CircleImage),
     returnType: typeof(double),
     defaultValue: 0.0,
     defaultBindingMode: BindingMode.OneWay,
     propertyChanged: (bindable, oldValue, newValue) =>
     { });

// Gets or sets value of this BindableProperty
public double ImageSize
{
    get => (double)GetValue(ImageSizeProperty);
    set => SetValue(ImageSizeProperty, value);
}
#endregion

```

Nous avons la propriété `ImageSize` qui sera exposée et sur laquelle on viendra "binder" dans le XAML.

La gestion du binding proprement dit est laissée à la `BindableProperty` `ImageSizeProperty` instanciée par la méthode statique  `BindableProperty.Create` avec les arguments suivants : 

- **propertyName** : nom de la propriété exposée, celle qui recevra les valeurs via Binding
- **declaringType** : type de la classe portant la propriété, ici CircleImage
- **returnType** : type retourné par la propriété
- defaultValue : valeur par défaut
- defaultBindingMode : définit le sens du binding par défaut
- coerceValue : permet de réévaluer la valeur d'une BindableProperty quand la valeur d'une autre BindableProperty change. Pas évident de l'expliquer en une seule phrase, je vous recommande de [lire la documentation](https://docs.microsoft.com/fr-fr/xamarin/xamarin-forms/xaml/bindable-properties#coerce-value-callbacks) !
- propertyChanged : callback appelé quand la valeur du binding vient de changer
- propertyChanging : idem quand la valeur est en train de changer
- defaultValueCreator : permet de définir une valeur par défaut à partir d'une `Func`   

Seuls les trois premiers arguments sont obligatoires.

Tout ceci se place bien entendu dans le code-behind de la `View`.

Nous avons désormais une propriété `ImageSize` mais comment allons-nous l'utiliser en interne dans notre `CircleImage` ?

Il y a plusieurs façons de voir les choses. Dans des cas simples qui touchent directement l'aspect visuel, on peut le faire directement dans le XAML. Parfois, c'est plus pertinent d'agir via les callbacks `propertyChanged` ou `propertyChanging` de la BindableProperty qui offrent plus de souplesse et de liberté que XAML.

Je vais vous présenter les deux mais, bien entendu, dans le [projet GitHub](https://github.com/SylvainMoingeon/CircleImageDemo) je n'ai pu en laisser qu'un !

### Lier les propriétés bindables au XAML du contrôle 

Partons de notre `CircleImage` de base. 

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<Image x:Class="CircleImageDemo.CircleImage.CircleImage"
       xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       HeightRequest="128"
       WidthRequest="128">
    <Image.Clip>
        <EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64"
                         />
    </Image.Clip>
</Image>
```

L'enjeu est de réussir à lier la propriété `ImageSize` aux propriétés `HeightRequest` et `WidthRequest` de l'image, ainsi qu'aux `RadiusX`, `RadiusY` et `Center` de l'ellipse dont les valeurs sont la moitié de `ImageSize`.

Pour cela, nous allons utiliser un Binding. Cela peut paraître un peu confus, il faut suivre un peu : on va binder en interne une valeur qui provient d'un Binding externe. Une fois qu'on a compris ça, on a tout compris.

Sauf que là, comme ça, ça ne fonctionnera pas. Il est nécessaire de préciser à notre `View` que son contexte de binding est... elle-même !

Une méthode simple est de donner un nom à notre contrôle, "this" par exemple pour mimer le mot clé "this" du C# et de donner "this" comme source du contexte de binding :

```xml
<Image x:Name="this" BindingContext="{x:Reference this}" ...>

```

On peut désormais binder `ImageSize` aux propriétés de l'`Image` :

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<Image x:Name="this"
       [...]
       BindingContext="{x:Reference this}"
       HeightRequest="{Binding ImageSize}"
       WidthRequest="{Binding ImageSize}"
       >
    [...]
</Image>
```

Ça y est, nous avons une image carrée de côté `ImageSize`. 

Vous la voulez circulaire ? Fastoche, il suffirait de binder (ImageSize / 2) sur les propriétés de l'ellipse... si seulement c'était possible !

Il nous faut trouver un moyen de modifier la valeur du binding à la volée... Ça tombe bien, Xamarin.Forms nous propose un outil pour cela : le `Converter`.

Je n'entre pas dans les détails car j'ai un article en cours de rédaction sur le sujet, sachez simplement que le principe est simple : il s'agit d'une classe implémentant l'interface `IValueConverter`, dont la principale méthode reçoit une valeur en entrée et en retourne une autre en sortie. Exactement ce qu'il nous faut. Nous allons créer un `DividerConverter` !

Pour rendre le converter plus versatile, celui-ci expose une propriété `Divider` définissant la quantité par laquelle nous souhaitons diviser. Nous aurions pu simplement diviser par deux, en dur dans le code, mais ça limiterait la réutilisation du converter.

La méthode `Convert()` divise la valeur reçue de l'extérieur, la méthode `ConvertBack()` fait l'inverse dans le cas d'un binding à double sens (si on modifie la valeur au niveau de l'interface utilisateur, elle sera répercutée dans le code, mais bien entendue avec l'opération arithmétique inverse !)

```csharp
public class DoubleDividerConverter : IValueConverter
{      
    public int Divider { get; set; } = 1;

    // Divise la valeur reçue par Divider
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double doubleToDivide)
        {
            return doubleToDivide / Divider;
        }

        throw new ArgumentException($"{nameof(value)} should be of type double");
    }

    // Multiplie la valeur renvoyée par Divider
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double doubleToMultiply)
        {
            return doubleToMultiply * Divider;
        }

        throw new ArgumentException($"{nameof(value)} should be of type double");
    }
}

```

Reste à utiliser le Converter dans le XAML :

- Le déclarer comme ressource statique dans la `View`
- L'ajouter aux Bindings
- Préciser à l'`EllipseGeometry` son contexte de Binding.

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<Image x:Name="this"
         [...]
       >
    <Image.Resources>
        <ResourceDictionary>
            <local:DoubleDividerConverter 
                    x:Key="DoubleDividerConverter" 
                    Divider="2" />
        </ResourceDictionary>
    </Image.Resources>

    <Image.Clip>
        <EllipseGeometry BindingContext="{x:Reference this}"
                         RadiusX="{Binding ImageSize, Converter={StaticResource DoubleDividerConverter}}"
                         RadiusY="{Binding ImageSize, Converter={StaticResource DoubleDividerConverter}}"
                         Center="??? Center attend un type Point, je vous laisse réfléchir quelques instants"
                         />
    </Image.Clip>
</Image>
```

C'est bon pour la forme de l'ellipse, nous venons d'en faire un cercle de rayon (Imagesize / 2).

Reste à la positionner via la propriété `Center`. Celle-ci attend une valeur de type `Point`. Nous allons là encore utiliser un converter. Il recevra la taille de l'image en entrée et retournera un point dont les coordonnées sont au centre de l'image.

Vous trouverez tout le code dans le [dépôt GitHub](https://github.com/SylvainMoingeon/CircleImageDemo). 

### Gérer la propriété bindable dans le code behind 

Si la beauté du XAML vous laisse de marbre, il est possible d'effectuer la même chose directement dans le code-behind de la View.

Il suffit de placer notre code dans l'événement `propertyChanged` de la `BindableProperty` : nous y définirons le rayon du cercle, la taille de l'image et nous appliquerons l'ellipse à l'image après avoir créé celle-ci.

Et c'est tout !

```csharp
public static readonly BindableProperty ImageSizeProperty =
  BindableProperty.Create(
     propertyName: nameof(ImageSize),
     declaringType: typeof(CircleImage),
     returnType: typeof(double),
     defaultValue: 0.0,
     defaultBindingMode: BindingMode.OneWay,
     propertyChanged: (bindable, oldValue, newValue) =>
     {
         if (bindable is CircleImage circleImage && newValue is double imageSize)
         {
             double radius = imageSize / 2;
             EllipseGeometry ellipseGeometry = new EllipseGeometry()
             {
                 Center = new Point(radius, radius),
                 RadiusX = radius,
                 RadiusY = radius
             };

             circleImage.HeightRequest = imageSize;
             circleImage.WidthRequest = imageSize;
             circleImage.Clip = ellipseGeometry;
         }
     });

```

Vous allez me dire, ça valait bien la peine de s'embêter en XAML avec des converters ! Ce n'est pas complètement faux. Mais pas totalement vrai.

La qualité d'un code ne se limite pas à sa simplicité mais aussi à sa clarté. Ici, un développeur ne connaissant pas le code devra chercher à l'aveuglette dans le code-behind pour comprendre comment l'image est dimensionnée et devient circulaire. Tant que ça reste simple, ça passe. Dans un code plus touffu, cela peut vite devenir un véritable casse-tête !

Il y a également un risque accru de [monstre spaghetti et de crotte de nez dans le code](https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4). Un développeur débutant ou peu consciencieux, aura vite fait de mélanger tout et n'importe quoi dans `propertyChanged`. Au minimum, il aurait fallu que j'extrais le code dans une méthode à part. Et pour être parfaitement propre, séparer le dimensionnement de l'image du découpage circulaire dans deux méthodes distinctes. Et tant qu'on y est, placer la conversion entre la taille de l'image et l'ellipse dans une classe à part pour que ce soit réutilisable. Une sorte de `Converter` qui cacherait son nom, en quelque sorte... Bref, le code-behind est plus simple surtout parce qu'on peut librement y coder comme un sale.

> L'avantage de la méthode "XAML", c'est que 100% du code concernant l'aspect visuel est au même endroit, directement là où on irait le chercher spontanément. Tout y est explicite, on sait d'emblée où chercher l'information. Et ça, en débogage, ça n'a pas de prix !

## Appliquons le même principe pour créer un contrôle avancé 

Nous avons désormais une image circulaire simple et réutilisable. Nous allons de même créer une image circulaire encapsulant la précédente pour lui ajouter quelques options.

Pour commencer, nous allons créer comme précédemment un `ContentView` que nous nommerons `AdvancedCircleImage` et qui héritera de `Grid` puisque c'est le `Layout` de base de notre contrôle.

Je vous laisse faire, c'est exactement comme tout à l'heure, avec une `Grid` à la place de l'`Image`.

A l'intérieur de notre Grid, nous aurons donc : 

- Deux itérations de notre `CircleImage`, l'une pour l'image, l'autre pour l'image de substitution
- Une `Ellipse` pour la bordure
- Un `ActivityIndicator` pour l'indicateur de chargement

Dans le code-behind, nous définirons quelques `BindablePropertie` :

- MainImageSource : source de l'image principale
- PlaceholderImageSource : source du placeholder
- ImageSize : taille de l'image
- IsBorderVisible : la bordure est-elle visible ?
- BorderColor : couleur de la bordure
- BorderThickness : épaisseur de la bordure
- IsLoaderEnabled : l'indicateur de chargement est-il actif (si faux on ne le verra jamais, si vrai on le verra quand l'image est en train de charger)  

Nous allons appliquer la même recette que précédemment, voici une version simplifiée du XAML de laquelle j'ai retiré ce qui n'est pas nécessaire à la compréhension :

```xml
<Grid x:Name="this">
    <Grid.Resources>
        <DoubleDividerConverter x:Key="DoubleDividerConverter" Divider="2" />
        <ImageSizeToCenterConverter x:Key="ImageSizeToCenterConverter" />
        <ColorToBrushConverter x:Key="ColorToBrushConverter" />
    </Grid.Resources>

    <local:CircleImage BindingContext="{x:Reference this}"
                       ImageSize="{Binding ImageSize}"
                       Source="{Binding PlaceholderImageSource}"/>

    <local:CircleImage x:Name="Image"
					 BindingContext="{x:Reference this}"
                     ImageSize="{Binding ImageSize}"
                     Source="{Binding MainImageSource}" />

    <Ellipse BindingContext="{x:Reference this}"
             Stroke="{Binding BorderColor, Converter={StaticResource ColorToBrushConverter}}"
             StrokeThickness="{Binding BorderThickness}"
             HeightRequest="{Binding ImageSize}"
             WidthRequest="{Binding ImageSize}"
             IsVisible="{Binding IsBorderVisible}"/>

    <ActivityIndicator IsRunning="{Binding Source={x:Reference Image}, Path=IsLoading}"
                       BindingContext="{x:Reference this}"
                       IsVisible="{Binding IsLoaderEnabled}"
                       Color="{Binding BorderColor}"/>
</Grid>
```

Vous noterez juste l'apparition d'un nouveau `Converter` : notre propriété `BorderColor` transmet un type `Color` alors que la propriété `Stroke` de l'Ellipse attend une `Brush`.

Pour le reste, ce n'est que du `Binding` vers les `BindableProperty` et des astuces que nous avons déjà vu dans la [première partie de l'article](https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-1-4mgo).

Nous avons désormais une image circulaire avancée parfaitement réutilisable dans notre projet ! Chouette !

Nous en avons même deux : `CircleImage` et `AdvancedCircleImage` !

## Utiliser le contrôle dans le code 

En réalité, vous savez déjà comment utiliser votre contrôle personnalisé puisque vous l'avez déjà fait en incorporant `CircleImage` dans le XAML de `AdvancedCircleImage`. 

Il suffit de :

1. Déclarer le namespace auquel appartient votre contrôle dans votre `ContentPage`
2. Utiliser le contrôle dans le XAML

Voici un exemple très simplifié :

```xml
<ContentPage x:Class="CircleImageDemo.MainPage"
            xmlns:ci="clr-namespace:CircleImageDemo.CircleImage"
            [...]>            
    <ci:AdvancedCircleImage ImageSize="64"
                            MainImageSource="{Binding PhotoUrl}"
                            PlaceholderImageSource="{Binding PlaceholderImage}"
                            BorderColor="Yellow"
                            BorderThickness="4"
                            IsBorderVisible="{Binding IsBookmarked}"
                            IsLoaderEnabled="True" />
</ContentPage>

```

Dans le [projet de démonstration](https://github.com/SylvainMoingeon/CircleImageDemo), j'utilise l'`AdvancedCircleImage` dans une liste (en fait un [BindableLayout](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/bindable-layouts) mais ce n'est pas le sujet).

## Le projet de démonstration 

Je vous redonne le lien vers le dépôt GitHub du projet : [https://github.com/SylvainMoingeon/CircleImageDemo](https://github.com/SylvainMoingeon/CircleImageDemo) ainsi que la petite animation :

![](https://www.sylvainmoingeon.fr/media/posts/37/demo-circleimage-2.gif)

Celle-ci simule une page de contacts qui existe avant tout pour illustrer le contrôle personnalisé que nous venons de créer.

L'`AdvancedCircleImage` est utilisée pour afficher les photos de contacts. Si l'image n'est pas renseignée ou indisponible (url invalide par exemple), le contrôle affiche une image de substitution par défaut à la place.

Au clic sur une fiche contact, celui-ci bascule de l'état "mis en favori" à l'état "pas mis en favori", ce qui affiche ou non la bordure.

L'indicateur de chargement est fugace mais visible dans les deux dernières fiches puisqu'une image est définie dans chacune d'elle mais inaccessible (erreur 404 et url non joignable).

### Jetez-y un œil ! 

C'est tout en ce qui concerne l'image circulaire, mais il y a deux ou trois petites choses dans le code qui pourraient vous intéresser si vous n'êtes pas familier avec XAML et [MVVM](https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g). Et si vous êtes arrivé jusqu'à la fin de cet article, c'est sans doute le cas !

#### StringFormat 

Pour illustrer l'usage de [MVVM](https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g), j'ai ajouté un `Label` qui indique le nombre de contacts en favori. Celui-ci utilise `StringFormat` pour afficher une phrase complète intégrant la valeur obtenue via le `Binding`. [`StringFormat`](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/string-formatting) est un outil très puissant et malheureusement souvent négligé par les développeurs. Il vous évitera pas mal de [mauvaises bidouilles et de crottes dans le code](https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4).

#### Code / XAML spécifique à la plateforme 

Il y a quelques cas à la marge mais suffisamment fréquents où malheureusement un code 100% commun n'est plus suffisant. Cela tient surtout à des différences fonctionnelles entre les plateformes. 

Par exemple, l'encoche et le bouton virtuel apparus sur iOS à partir de l'iPhone X (il me semble). Lorsque vous définissez votre mise-en-page, vous risquez fort d'obtenir quelque chose comme ceci :

![](https://www.sylvainmoingeon.fr/media/posts/37/no-safe-area-2.png)

Il existe quelques fonctionnalités spécifiques aux plateformes directement accessibles dans le code commun, notamment pour [iOS](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/ios/) et [Android](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/android/). Ici, nous allons nous intéresser au `SafeArea` d'iOS.

Dans le XAML :

```xml
<ContentPage 
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="True"
[...]>
    [...]
</ContentPage>
```

Ce qui corrigera le problème en protégeant les zones de l'encoche et du bouton virtuel pour éviter ces affreux chevauchements :

![](https://www.sylvainmoingeon.fr/media/posts/37/safe-area.png)

> Fouillez dans la documentation, il y en a d'autres qui vous éviterons prises de tête et tentatives de corriger ces choses là vous même en codant dans les projets natifs.

#### DesignTimeVisible 

Si vous utilisez le [Previewer Xamarin.Forms](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-previewer/?pivots=windows), vous constaterez que vos contrôles personnalisés n'apparaissent pas dedans, ce qui peut être gênant lorsque vous êtes en train de dessiner vos interfaces.

![Copie d'écran présentant le previewer Xamarin.Forms, le contrôle personnalisé n'est pas affiché](https://www.sylvainmoingeon.fr/media/posts/37/designtime-not-visible.png)<figcaption>L'image circulaire n'est pas affichée pas dans le previewer, quel malheur !</figcaption>

La plupart du temps, ça ne tient qu'à une ligne de code ! Un simple attribut à ajouter à votre classe : `[DesignTimeVisible(true)]`

Comme son nom l'indique de façon assez explicite, cet attribut indique si la classe doit être traitée ou non par le previewer.

```xml
[XamlCompilation(XamlCompilationOptions.Compile)]
[DesignTimeVisible(true)]
public partial class AdvancedCircleImage : Grid
{
[...]
}
```

Pensez bien à l'ajouter à chacun de vos contrôles personnalisés ! Ici au CircleImage et à l'AdvancedCircleImage. Voici le résultat, une fois l'attribut ajouté à chacune des classes : 

![Illustration du previewer avec l'attribut DesignTimeVisible](https://www.sylvainmoingeon.fr/media/posts/37/designtime-visible.png)<figcaption>L'image circulaire est bien affichée grâce à l'attribut DesignTimeVisible(true)</figcaption>

## Conclusion 

Pour allez plus loin, il faudrait totalement sortir le contrôle personnalisé du projet et pourquoi pas en faire un package nugget. Peut-être une idée pour un prochain article !

Si vous avez des questions, des astuces ou même des remontrances concernant le sujet, je vous invite à laisser des commentaires. Juste là dessous. ⬇