<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sylvain</title>
    <description>The latest articles on DEV Community by Sylvain (@sylvainmoingeon).</description>
    <link>https://dev.to/sylvainmoingeon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F145480%2F0e7a0d0d-2438-4d83-ad3d-79c366872815.jpeg</url>
      <title>DEV Community: Sylvain</title>
      <link>https://dev.to/sylvainmoingeon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sylvainmoingeon"/>
    <language>en</language>
    <item>
      <title>Je n'aime pas coder ! 😱</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Wed, 27 Nov 2024 10:03:35 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/je-naime-pas-coder--3p4h</link>
      <guid>https://dev.to/sylvainmoingeon/je-naime-pas-coder--3p4h</guid>
      <description>&lt;p&gt;Je suis développeur et je n'aime pas coder.&lt;/p&gt;

&lt;p&gt;Entendons nous bien. Je ne suis pas en train d'avouer un désamour du code, non, mais plutôt mon attrait pour d'autres aspects du développement logiciel.&lt;/p&gt;

&lt;p&gt;De façon générale, ce que j'aime, c'est créer, concevoir, mettre en œuvre mes compétences et mon talent au service d'un projet pour le voir se réaliser, brique par brique. Qu'il s'agisse de développement informatique est un accident. J'aime les sciences et technologies, mon père a toujours été un féru de d'informatique, nous avons eu à la maison les premières consoles de jeux (Vidéopac !), les premiers ordinateurs grand public (amstrad !). Il était donc logique qu'à un moment ou un autre, j'atterrisse dans ce milieu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Philips_Videopac+_G7400" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fol9e7f9r3a6f1j1d448u.jpg" alt="Photo d'une console Videopac+" width="550" height="414"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mon premier contact avec l'informatique ! (credits Marco van den Hout, license CC BY-SA 3.0)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mais, avec le recul, je me rends compte que je ne fais pas ce métier par amour du code ou même de l'informatique, mais surtout par gout de voir un projet aboutir, d'y participer de bout en bout, de le voir grandir et de le maintenir. C'est pour cette raison, sans doute, que je n'ai jamais pu me résoudre à devenir un sniper du code en alignant les missions courtes. Cela ne m'intéresse pas.&lt;/p&gt;

&lt;p&gt;Ce que j'aime également, c'est le travail bien fait, peu importe le sujet. Dans mon métier, j'attache bien plus d'importance aux bonnes pratiques de programmation, au soin d'un code propre et lisible, qu'à la technique pure et à la technologie.  &lt;/p&gt;

&lt;p&gt;J'ai dans ma carrière eu trop à faire à des algorithmes savants mais impossible à entretenir. A des frameworks perfectionnés où l'on passe plus de temps à contourner les bugs internes à ceux-ci qu'à faire avancer le projet.  &lt;/p&gt;

&lt;p&gt;J'ai trop souvent travaillé sur du code écrit par des développeurs "certifiés Microsoft Plus Mieux" qui, certes, maîtrisent leur framework jusqu'au bout des doigts mais étaient incapables d'assembler deux morceaux de code sans que tout explose. Je les ai vu parfois user de tout leur savoir technique pour masquer (!) des bugs qui n'auraient jamais existés si ça n'avait pas été architecturé avec le cul au départ.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuya1rhrmql61d5m34e3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuya1rhrmql61d5m34e3.jpg" alt="Un câblage électronique inextricable" width="620" height="465"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Un jour, on m'a demandé de déboguer un code qui ressemblait à ça&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Le code est un outil et je l'utilise du mieux que je peux. Parfois ça m'éclate, parfois ça m'émerveille, parfois ça me rend dingue et me donne envie de pleurer.&lt;/p&gt;

&lt;p&gt;Mais mon métier n'est pas codeur.&lt;/p&gt;

&lt;p&gt;Je suis développeur et je code parce c'est l'outil en vogue pour ce métier. Demain ce sera l'IA ? Ça me va aussi. Du nocode ? Pourquoi pas. Coder avec des allumettes ? C'est parti !  &lt;/p&gt;

&lt;p&gt;Quand j'étais petit je "fabriquais" des histoires avec Playmobil, je construisais des vaisseaux spatiaux avec Lego. Puis au collège, j'ai découvert l'informatique et j'ai commencé à assembler des PC. Puis la musique et je me suis mis à composer (sous DOS avec le clavier de l'ordinateur !). Et récemment, je me suis formé à la lutherie et&lt;a href="https://www.lutheriemoingeon.fr/" rel="noopener noreferrer"&gt;je façonne des guitares&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0soya1hqcuqe7kfg4iyn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0soya1hqcuqe7kfg4iyn.jpg" alt="Interface du logiciel DOS Voyetra Sequencer" width="642" height="304"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;J'ai composé mes premières musiques sur ce type d'interface. Ce n'est clairement pas l'outil qui m'a attiré !&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dans tous les cas, ce qui m'anime, c'est apprendre, découvrir, puis utiliser ce que j'ai appris pour construire, fabriquer, concevoir.&lt;/p&gt;

&lt;p&gt;Malheureusement, on vit dans un système qui t'enferme dans des cases, je suis développeur, on attend donc de moi que j'aime coder et qu'il s'agisse du critère premier dans ma recherche d'emploi. Mon cerveau ne fonctionne pas comme ça. J'utilise le code pour créer du logiciel comme j'utilise la râpe ou le rabot pour &lt;a href="https://www.lutheriemoingeon.fr/" rel="noopener noreferrer"&gt;façonner le manche d'une guitare&lt;/a&gt;. Je ne suis pas passionné de râpe et de rabot. Mais j'aime &lt;a href="https://www.lutheriemoingeon.fr/" rel="noopener noreferrer"&gt;construire un instrument de musique&lt;/a&gt; et m'émerveiller quand un musicien le joue une fois terminé.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1t9f87hi8ix34a54zi5a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1t9f87hi8ix34a54zi5a.jpg" alt="Couverture du livre The Pragmatic Programmer" width="800" height="1018"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mais que vois-je ? Un rabot pour illustrer un livre d'informatique !&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;C'est ma vision des choses, tout le monde ne la partagera pas, c'est évident. Certains même ne comprendront pas de quoi je parle, je le sais, j'en ai croisé plein.&lt;/p&gt;

&lt;p&gt;Et toi, tu en penses quoi ? Qu'est-ce qui t'anime dans ton métier ? Les outils que tu utilises ? La manière de les utiliser au mieux ? Ou juste contribuer à un projet qui te passionne, peu importe le moyen ?&lt;/p&gt;

</description>
      <category>bonnespratiques</category>
      <category>goodpractices</category>
      <category>french</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>De développeur à luthier, histoire d'une reconversion</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Tue, 20 Aug 2024 17:43:05 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/de-developpeur-a-luthier-histoire-dune-reconversion-j0o</link>
      <guid>https://dev.to/sylvainmoingeon/de-developpeur-a-luthier-histoire-dune-reconversion-j0o</guid>
      <description>&lt;p&gt;Voilà un peu plus d'une année que je n'ai écrit une ligne de code ! Je vais y revenir promis, mais à temps-partiel uniquement.&lt;/p&gt;

&lt;p&gt;Pourquoi à temps-partiel ? C'est une longue remise en question qui a débutée en juin 2023 et j'aimerais en apporter le témoignage. Laissez-moi vous raconter l'histoire…  &lt;/p&gt;

&lt;p&gt;Depuis plus de 15 ans je suis développeur informatique. J'ai travaillé en TPE, dans un grand groupe, en tant qu'indépendant, puis dans une start-up. Mais comme cela arrive souvent, la start-up subit un (et même plusieurs) mauvais coups et met la clé sous la porte.  &lt;/p&gt;

&lt;p&gt;C'est ainsi que je me retrouve licencié économique en juin 2023. Je pourrais, étant donné le marché du travail dans mon domaine et mon expertise, retrouver rapidement un poste mais je décide de profiter de la période de chômage pour sortir la tête du guidon et prendre un peu de recul.   &lt;/p&gt;

&lt;p&gt;Et ce que je découvre n'est pas beau ! Ça craint !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F43%2Fburnout.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F43%2Fburnout.webp" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bien qu'aimant mon job, je me rends compte que je ne suis absolument pas épanoui. J'aime coder, développer, voir un projet évoluer et prendre forme, mais les conditions de travail me rendent malades :  &lt;/p&gt;

&lt;p&gt;Je ne supporte plus d'être assis toute la journée derrière un écran, ça me rend aigri, le soir je suis complètement sous tension, la tête prête à exploser.  &lt;/p&gt;

&lt;p&gt;Au fil du temps, mon corps développe toutes sortes de troubles musculo-squelettiques, j'ai en permanence mal aux mains, aux coudes, au dos, aux hanches et aux genoux.   &lt;/p&gt;

&lt;p&gt;Tous les soirs, je passe plus d'une heure à effectuer des exercices de mobilité et d'étirement pour réussir à atténuer la douleur. Sans quoi je ne dors pas la nuit. Tout ce temps, je pourrais le passer avec ma famille ou profiter de mes loisirs.  &lt;/p&gt;

&lt;p&gt;En journée je me rends à mes séances d'orthoptie parce qu'à force de fixer un écran  en permanence, mes yeux ont développé un strabisme et une incapacité à se relâcher. J'ai les yeux lourds et mal au crâne du matin au soir.  &lt;/p&gt;

&lt;p&gt;Je me rends subitement compte que mes deux bébés sont des ados. Je ne les ai pas vu grandir.  &lt;/p&gt;

&lt;p&gt;Et en plus, j'ai 45 ans, l'heure de la crise !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstr7awadcre3ehahvvhq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstr7awadcre3ehahvvhq.jpeg" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans le cadre de mon licenciement, j'intègre le Contrat de Sécurisation Professionnel de Pole Travail (j'ai commencé avec l'un, terminé avec l'autre) qui délègue la tâche à la société Solerys.  &lt;/p&gt;

&lt;p&gt;A ce moment-là, je suis complètement paumé. Je ne sais plus où je suis, ni où je vais. Ni dans quel état j'erre (j'ai le droit au "dad jokes", j'ai l'âge et les enfants requis). J'ai un dégout profond des écrans, je ne regarde même plus la TV. Impossible pour moi de m'imaginer retourner dans l'informatique.  &lt;/p&gt;

&lt;p&gt;Dans le brouillard total, je tourne en rond, je ne sais quoi faire. Ou plutôt, je sais trop quoi faire mais dans quelle direction me tourner ? Entre rêves inassouvis et nombreux centres d'intérêts, je me dis pourquoi pas ? Mais quoi ?  &lt;/p&gt;

&lt;p&gt;Lors de mon suivi, j'ai la chance de tomber sur une consultante à l'écoute et ouverte (coucou Lydie). Lorsqu'elle me demande si j'ai des projets, je me surprends moi-même à lui en sortir toute une liste :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faire de la photo, une de mes passions laissée à l'abandon faute de temps&lt;/li&gt;
&lt;li&gt;Capitaliser sur mes études scientifiques (BAC+5) pour revenir dans ce domaine
&lt;/li&gt;
&lt;li&gt;Me professionnaliser au niveau du sport (je suis animateur bénévole dans un club d'arts-martiaux)
&lt;/li&gt;
&lt;li&gt;Trouver une activité en lien avec la musique (passionné de guitare)
&lt;/li&gt;
&lt;li&gt;Me lancer dans un métier manuel, tiens pourquoi pas en rapport avec la guitare (je me souviens à cette occasion être passé plusieurs fois devant des ateliers de luthier en soupirant "pourquoi pas moi ?")
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;S'ensuit un accompagnement, avec bilan de compétences, profil psychologique, recherche d'un point d'accroche entre mes centres d'intérêts, mes qualités, mes aspirations et un éventuel projet professionnel.  &lt;/p&gt;

&lt;p&gt;Et peu-à-peu, celui-ci se dessine, la balance penche de plus en plus vers la lutherie.  &lt;/p&gt;

&lt;p&gt;C'est ainsi que je me retrouve en formation dans un atelier de facture instrumentale jusqu'en aout 2024. Et c'est le bonheur total. :-)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20231128_184139.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14xyratbtcd4yw2k6az8.jpg" alt="Des instruments de musique pendus au plafond dans un atelier de lutherie" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Ambiance au centre de formation&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20231129_112248.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F263agqezzzzi5jcty1xe.jpg" alt="Photo illustrant des moules utilisés pour fabriquer des guitares" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Une belle collection de moules&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240301_095101.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffctampmt1gr3fz7740bz.jpg" alt="Photo illustrant le cintrage d'une éclisse" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Un pan coupé qui m'a donné du fil (ou plutôt du bois) à retordre&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240301_160706.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45h5ojj2xfagykuprovq.jpg" width="768" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240402_154406.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9pgq7qneq9zrdc20vyu.jpg" alt="Photo illustrant un ciseau à bois affleurant un filet dans une rosace de guitare" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Affleurage d'un filet de rosace&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240404_165316.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonasfxmhycmrlzcmqukl.jpg" alt="Photo illustrant un collage au ciel" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Collage au ciel&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240405_162705.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d2lwbszsdooa9y1i826.jpg" alt="Photo illustrant les barrages de table d'harmonie dans une guitare" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Présentation de la table d'harmonie sous les éclisses. Ne paniquez pas les puristes, les barrages ne sont pas encore ajustés ni scalopés&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240411_165205.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yqbvjnkwzb5yukp49x0.jpg" alt="Photo illustrant une vielle à roue de 1742 et sa réplique exacte" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Une très vieille vielle (1742) et sa réplique en vue de conservation&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240513_175718.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbwhqdyhpqqgsagrhq0y.jpg" alt="Photo illustrant le collage des filets d'éclisse sur une guitare classique" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Collage des filets avec la technique ultra moderne et sophistiquée des "bouts de scotch"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240516_121424.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feuviiji6v18tnrat5zby.jpg" alt="Photo illustrant le chanfrein de confort d'une guitare classique" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Chanfrein de confort&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240607_152241.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fby2zrcs4pw7m9updqd04.jpg" alt="Photo illustrant un luthier travaillant au racloir" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;On s'occupe du sillon interfessier de la guitare (vocabulaire très officiel, il va sans dire)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240613_120653.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx7tyu5537bf3e8vrovu.jpg" alt="Photo illustrant le profilage d'un manche de guitare" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Profilage du manche&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240613_161832.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kqgggrk8t4yq0uu10cz.jpg" alt="Photo illustrant l'emboitement du manche sur la caisse" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Emboitement du manche sur la caisse&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_164653.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0hbzxx2puf0lg5s9azr.jpg" alt="Photo illustrant le talon de la guitare" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Décoration du talon en érable pour la continuité avec les filets&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_164717.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fun7yq27lpzxmlt37w1nb.jpg" alt="Photo illustrant le talon de la guitare" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Décoration du talon en érable pour la continuité avec les filets&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_175523.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbnm1ehq4evynq7nk2yg.jpg" alt="Photo illustrant une guitare assemblée mais pas encore terminée" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Ça semble presque terminé, mais comme en informatique, quand c'est presque fini, ça ne fait que commencer !&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240627_111132.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28rkuiac9ck1ec12fmvg.jpg" alt="Photo illustrant l'usinage d'un chevalet de guitare" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Usinage du chevalet&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240702_162427.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3x9br7j0wu2gw33qm73y.jpg" alt="Photo illustrant le chevalet terminé" width="576" height="768"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Le chevalet prêt à être posé&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240705_110756.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flugz9yxug3jiswbfrmd7.jpg" alt="Photo illustrant le collage du chevalet" width="768" height="576"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Collage du chevalet (la guitare a été vernie entre temps)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enfin, nonobstant l'éloignement de ma famille (ce n'est pas à côté de chez moi) et les frais considérables, en moyenne 500€ par mois pour le logement et le carburant. Quand on vit sur les allocations chômage, ça pique. Mais toucher son rêve du bout du doigt mérite bien quelques sacrifices, n'est-ce pas ?  &lt;/p&gt;

&lt;p&gt;J'y apprends le travail du bois, les bases de la fabrication d'instruments, en commençant petit (Ukulélé), puis viennent les guitares classiques et électriques. J'y vois aussi des techniques de réparations/restauration. Du réglage, mais ça je savais déjà faire. J'y découvre des instruments que je connaissais peu (balalaïka, bouzouki, vielle à roue) et d'autres qui m'étaient parfaitement inconnus (Nickelharpa).  &lt;/p&gt;

&lt;p&gt;J'apprends, je découvre, j'explore et je crée. Mes activités préférées en somme. Je me rends d'ailleurs compte que c'est aussi, au départ, ce qui m'avait poussé dans le développement logiciel. Ce n'est pas coder qui m'attirait, mais apprendre de nouvelles techniques, les appliquer pour construire quelque chose de nouveau. Observer un logiciel "pousser" sous mes yeux. Et avec le recul, je me rends compte que c'est la même pour la photo et l'enseignement des arts-martiaux : apprendre et appliquer ce que j'ai appris pour construire quelque chose de nouveau !  &lt;/p&gt;

&lt;p&gt;Aurai-je découvert mon Ikigaï ?  &lt;/p&gt;

&lt;p&gt;Mais voilà, le temps de la formation est révolu, mes pieds redescendent lentement sur terre. Il me reste 3 mois d'allocations chômage et je sais pertinemment qu'il me faudra du temps avant d'être rentable en tant que luthier et avoir une activité pérenne.&lt;/p&gt;

&lt;p&gt;Je reviens donc à la réalité : un travail que je sais faire qui paie bien et la lutherie comme complément. D'autant que ma conjointe est elle-même artisan et deux métiers indépendants pour une famille avec enfants, c'est trop dangereux. On en a fait les frais pendant la crise du covid (j'étais Freelance à cette époque), autant assurer un minimum de sécurité pour au moins l'un des deux.   &lt;/p&gt;

&lt;p&gt;Voilà pourquoi, après une année de remise en question, je suis de nouveau à l'écoute d’opportunités, à partir du mois de décembre et à temps-partiel uniquement.   &lt;/p&gt;

&lt;p&gt;Dans le développement informatique de préférence, puisque c'est mon domaine d'expertise, mais après tout, je reste réceptif à d'autres propositions, cette dernière année ayant eu l'effet de considérablement m'ouvrir l'esprit.&lt;/p&gt;

&lt;p&gt;Mon atelier de lutherie est en cours d'aménagement, on m'a déjà confié une guitare à restaurer et j'en ai une en cours de fabrication (pas une commande malheureusement, c'est pour exposer).&lt;/p&gt;

&lt;p&gt;Mais d'ici à ce que ça m'occupe à plein temps, il y a, je pense, matière à ce que je participe activement à d'autres projets. 😉&lt;/p&gt;

&lt;p&gt;Un développeur qui fabrique des guitares, qui a des rêves et se donne les moyens de les réaliser, ça vous intéresse ? contactez-moi !&lt;/p&gt;

&lt;p&gt;Un luthier qui a l'esprit cartésien, le sens du détail et l'esprit d'analyse d'un développeur informatique, ça vous intéresse ? contactez-moi aussi, c'est le même mec !&lt;/p&gt;

&lt;p&gt;Bien merci à toi qui a tout lu jusqu'ici !  &lt;/p&gt;

&lt;p&gt;Ah, et oui, zut : like, partage et tout ça, hein.&lt;/p&gt;

</description>
      <category>reconversion</category>
      <category>luthier</category>
      <category>lutherie</category>
      <category>guitare</category>
    </item>
    <item>
      <title>Authentification Azure Directory B2C dans une application Xamarin.Forms</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Fri, 19 Mar 2021 17:21:07 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/authentification-azure-directory-b2c-dans-une-application-xamarin-forms-ogd</link>
      <guid>https://dev.to/sylvainmoingeon/authentification-azure-directory-b2c-dans-une-application-xamarin-forms-ogd</guid>
      <description>&lt;p&gt;On m’a récemment demandé d’étudier la faisabilité d’une authentification depuis Azure Directory B2C dans une application Xamarin.Forms.&lt;/p&gt;

&lt;p&gt;La documentation officielle a un peu tendance à tourner en rond avec des liens en références circulaires, le portail Azure est vaste et pas super user-friendly (on peut passer beaucoup de temps à chercher une option qu’on est pourtant certain d’avoir aperçue cinq minutes plus tôt…).&lt;/p&gt;

&lt;p&gt;J’ai donc rédigé un guide rapide à l’attention des développeurs de l’équipe, je vous le livre tel quel, ce n’est donc pas nécessairement très bien rédigé, mais vous aurez toutes les informations pour débuter, depuis la création du &lt;em&gt;Directory B2C&lt;/em&gt; sur Azure jusqu’à l’authentification et la récupération des données de l’utilisateur dans l’application Xamarin.Forms.&lt;/p&gt;




&lt;h1&gt;
  
  
  Résumé
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Créer un tenant &lt;em&gt;Active Directory B2C&lt;/em&gt; dans Azure.&lt;/li&gt;
&lt;li&gt;Enregistrer l’application mobile dans le tenant &lt;em&gt;Active Directory B2C&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Créer des &lt;em&gt;User Flows&lt;/em&gt; avec des polices de &lt;em&gt;Signin&lt;/em&gt;, réinitialisation de mot de passe…&lt;/li&gt;
&lt;li&gt;Utiliser la &lt;em&gt;Microsoft Authentication Library (MSAL)&lt;/em&gt; pour gérer le flux d’authentification dans l’application.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/authentication/azure-ad-b2c" rel="noopener noreferrer"&gt;Authenticate Users with Azure Active Directory B2C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant" rel="noopener noreferrer"&gt;Tutorial: Create an Azure Active Directory B2C tenant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga" rel="noopener noreferrer"&gt;Tutorial: Register a web application in Azure Active Directory B2C&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Côté serveur
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Création du compte Azure
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aka.ms/azfree-docs-mobileapps" rel="noopener noreferrer"&gt;https://aka.ms/azfree-docs-mobileapps&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pré-requis :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un compte microsoft&lt;/li&gt;
&lt;li&gt;Une carte bancaire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je ne détaille pas, il suffit de suivre les étapes. Sachez simplement que vous disposez d’un crédit de 170€ pour débuter, de quoi faire des tests et de l’autoformation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration du portail en ANGLAIS
&lt;/h2&gt;

&lt;p&gt;Très important, peut-être un bug temporaire, mais si vous conservez le portail en français, la création de AD B2C échouera avec un message abscons vous indiquant que la valeur de &lt;em&gt;l’emplacement du groupe de ressource&lt;/em&gt; est invalide alors qu’on la sélectionne dans une liste (et peu importe la valeur sélectionnée) !&lt;/p&gt;

&lt;p&gt;C’est simplement que la liste est traduite et que visiblement quelque part dans le code, Azure attend une valeur en dur tirée de la liste en anglais. Franchement, j’ai honte pour Microsoft !&lt;/p&gt;

&lt;p&gt;Donc évitons-nous bien des soucis et passons immédiatement le portail en anglais.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9cii1bjjnlmli82jd2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9cii1bjjnlmli82jd2h.png" alt="Configuration du portail en anglais" width="382" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enregistrement du namespace &lt;em&gt;Mirosoft.AzureActiveDirectory&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Aucun tuto ne vous le dira, et c’est peut-être un bug temporaire, mais si vous ne le faites pas maintenant, vous obtiendrez cette erreur à la toute fin du processus et vous devrez tout recommencer. :-)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faoxksmdp13xfhzbbjnzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faoxksmdp13xfhzbbjnzb.png" alt="Message d'erreur concernant Microsoft.AzureActiveDirectory" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cliquez sur &lt;em&gt;Subscriptions&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8x86nn72zrw9ldy5cpi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8x86nn72zrw9ldy5cpi.png" alt="Navigation vers les subscriptions" width="583" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dans la liste, sélectionnez votre subscription courante.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si vous venez de créer un compte gratuit, il n’y en aura qu’une :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhk4uznlkq52rkt8l997l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhk4uznlkq52rkt8l997l.png" alt="Sélection de la subscription" width="288" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dans le panneau de gauche, cliquez sur &lt;em&gt;Resource providers&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4il01gw65ddgb7ed37sq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4il01gw65ddgb7ed37sq.png" alt="Navigation vers les resource providers" width="275" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cherchez Azure, sélectionnez &lt;em&gt;Microsoft.AzureActiveDirectory&lt;/em&gt; et cliquez sur Register&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pqtgcysb2slgzoxcdbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pqtgcysb2slgzoxcdbj.png" alt="Recherche de Microsoft.AzureActiveDirectory" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Patientez… patientez… patientez… patientez… Cliquez de temps en temps sur &lt;em&gt;Refresh&lt;/em&gt; au cas où. Si ça ne fonctionne pas, recommencez (sélection, register), au bout d’un moment ça finit par passer. :-)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F42%2F941ed15f975649aca20eeb624c4192fd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F42%2F941ed15f975649aca20eeb624c4192fd.png" alt="Microsoft.AzureActiveDirectory est enregistré" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;C’est bon, on y est, on peut commencer !&lt;/p&gt;

&lt;h2&gt;
  
  
  Création de l’Active Directory B2C
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cliquez sur &lt;em&gt;Create a resource&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0suf9ms6mzh66lrwcseq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0suf9ms6mzh66lrwcseq.png" alt="Créer une ressources" width="229" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cherchez et sélectionnez &lt;em&gt;Active Directory B2C&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfpx325fp0y5p46qdn5e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfpx325fp0y5p46qdn5e.png" alt="Sélectionner Active Directory B2C" width="557" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cliquez sur &lt;em&gt;Create&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F983ujy12kx6cochwhwzs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F983ujy12kx6cochwhwzs.png" alt="Cliquer sur Create" width="461" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a New Azure AD B2C tenant&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhzv5jai3ck8fgd6d6jp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhzv5jai3ck8fgd6d6jp.png" alt="Create a New Azure AD B2C tenant" width="564" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Renseignez les informations du tenant&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Créer un &lt;em&gt;Groupe de ressources&lt;/em&gt; avec le lien &lt;em&gt;Create New&lt;/em&gt; s’il n’en existe pas déjà un.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsl8g7fruaypynbi5v5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsl8g7fruaypynbi5v5p.png" alt="Renseigner les informations du tenant" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cliquez sur &lt;em&gt;Review + Create&lt;/em&gt; pour valider les informations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6d710cxhz6s9tbjknlq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6d710cxhz6s9tbjknlq.png" alt="Valider les informations" width="488" height="725"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cliquez sur &lt;em&gt;Create&lt;/em&gt; pour en finir !&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Patientez. Tant que ça gigote là-dedans c’est que ça bosse :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue2g04sfya943bl71t0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue2g04sfya943bl71t0e.png" alt="Indicateur d'activité dans la toolbar" width="71" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cliquez sur le lien, bienvenue à Black Mesa !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F42%2F000cf65ea90e48b0b774d8405119b298.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F42%2F000cf65ea90e48b0b774d8405119b298.png" alt="Lien menant vers votre tenant Active Directory B2C" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration d’une &lt;em&gt;application&lt;/em&gt; côté serveur
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Selection du tenant B2C
&lt;/h3&gt;

&lt;p&gt;Dans la toolbar en haut à droite, trouvez le bouton qui ressemble à un carnet avec un entonnoir :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6qgjbthajoh8tbux6sz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6qgjbthajoh8tbux6sz.png" alt="Buton de sélection des directories" width="426" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mais en principe vous devriez déjà être dessus si vous avez cliqué sur le lien à la fin de l’étape précédente.&lt;/p&gt;

&lt;p&gt;Si vous vous retrouvez sur une page d’accueil comme ça et que vous ne retrouvez pas votre &lt;em&gt;tenant AD B2C&lt;/em&gt;…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzv0kdn8i3ftc5bgb465.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzv0kdn8i3ftc5bgb465.png" alt="Page d'accueil du Directory" width="760" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Saisissez simplement &lt;em&gt;B2C&lt;/em&gt; dans la barre de recherche en haut et dans la rubrique &lt;em&gt;Services&lt;/em&gt;, sélectionnez &lt;em&gt;Azure AD B2C&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyy3orcwa6nmnuqgwbsq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyy3orcwa6nmnuqgwbsq7.png" alt="Recherche du tenant B2C" width="311" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enregistrement de l’application
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sélectionnez &lt;em&gt;App registrations&lt;/em&gt;…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtvimrea3vjtsnvxeylp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtvimrea3vjtsnvxeylp.png" alt="Navigation vers l'enregistrement d'applications" width="227" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Puis &lt;em&gt;New registration&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7gcatxr2n64c4fsn6uw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7gcatxr2n64c4fsn6uw.png" alt="Nouvel enregistrement" width="412" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Renseignez les informations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Nom&lt;/em&gt; et &lt;em&gt;type de compte&lt;/em&gt; supporté :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrje2vk5tx59tyyjse1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrje2vk5tx59tyyjse1b.png" alt="Nom et type de compte" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Redirect URI&lt;/em&gt; : Cela dépend du type d’application (application mobile, web app…). Dans notre cas, il s’agira d’une application mobile Xamarin.Forms. &lt;strong&gt;Attention, l’uri répond à un schéma bien particulier :&lt;/strong&gt; &lt;code&gt;msal[ce_que_vous_voulez]://auth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6rddrytgh8v0rff5bpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6rddrytgh8v0rff5bpd.png" alt="Redirect URi" width="791" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cet URi sera à renseigner dans les manifestes Android et iOS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Permissions :&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63w16p930csoakompz8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63w16p930csoakompz8k.png" alt="Permisssions" width="529" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Validez le tout en cliquant sur le bouton &lt;em&gt;Register&lt;/em&gt;. Bravo ! Votre application est enregistrée :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4d0n7dha5quaj6t2krq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4d0n7dha5quaj6t2krq.png" alt="Application enregistrée" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mais ce n’est pas fini : la case à cocher secrète
&lt;/h3&gt;

&lt;p&gt;Si vous vous en tenez là et suivez le tuto Microsoft et l’application &lt;em&gt;Sample&lt;/em&gt; fournie, cela ne fonctionnera pas : les URI utilisés comme point d’accès dans l’application ne sont pas autorisés par défaut !&lt;/p&gt;

&lt;p&gt;Rendez-vous dans la rubrique &lt;em&gt;Authentication&lt;/em&gt; et cochez les cases :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn64e86hi8xwchoi6ik0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn64e86hi8xwchoi6ik0t.png" alt="Cocher les Uri d'authentification" width="779" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pensez bien à sauvegarder. Le bouton en haut, je vous laisse le trouver tout seul.&lt;/p&gt;

&lt;h3&gt;
  
  
  Création des &lt;em&gt;User Flows&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Créer un &lt;em&gt;user flow&lt;/em&gt; revient à autoriser les utilisateurs à interagir avec leur compte AD : se connecter, réinitialiser son mot de passe, modifier son profile…&lt;/p&gt;

&lt;p&gt;NB : les flows sont partageables entre les applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sign In
&lt;/h4&gt;

&lt;p&gt;Au minima, les utilisateurs auront besoin de se connecter. Dans la page de votre &lt;em&gt;tenant AD B2C&lt;/em&gt;, allez sur &lt;em&gt;User flows&lt;/em&gt; dans la rubrique &lt;em&gt;Policies&lt;/em&gt; et cliquez sur &lt;em&gt;New user flow&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxglopigvydnyhj35o74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxglopigvydnyhj35o74.png" alt="New User Flow" width="267" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Puis sélectionnez &lt;em&gt;Sign In&lt;/em&gt; et créez le flow dans sa version recommandée.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbls8h9hl5xk050bgugyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbls8h9hl5xk050bgugyt.png" alt="Signin User Flow" width="680" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Renseignez les informations en fonction du mode de connexion et de l’authentification multiple souhaités.&lt;/p&gt;

&lt;p&gt;Notez bien le nom, vous en aurez besoin côté client :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sqsjokx3edliptaphy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sqsjokx3edliptaphy8.png" alt="Nom de la police" width="798" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La dernière rubrique permet de sélectionner les données qui seront récupérées côté client lors de la connexion utilisateur. Cliquez sur &lt;em&gt;Show more…&lt;/em&gt; pour obtenir la liste complète.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm20d6x99buyvb4z2bhhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm20d6x99buyvb4z2bhhc.png" alt="Claims" width="366" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finalisez en cliquant sur le bouton &lt;em&gt;Create&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Autres flows
&lt;/h4&gt;

&lt;p&gt;La procédure est similaire pour les autres types de &lt;em&gt;flows&lt;/em&gt;. Créez ceux dont vous avez besoin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Création des utilisateurs
&lt;/h3&gt;

&lt;p&gt;Sur la page de votre &lt;em&gt;tenant Azure AD B2C&lt;/em&gt;, cliquez sur &lt;em&gt;Users&lt;/em&gt; dans la rubrique &lt;em&gt;Manage&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthjvqiyzu38q077ddsnu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthjvqiyzu38q077ddsnu.png" alt="Création des utilisateurs" width="189" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensuite, cela dépend du mode de connexion envisagé. Il sera peut-être nécessaire de d’abord configurer un ou plusieurs fournisseurs d’identité (compte Microsoft, compte local, Github, Facebook…), dans la même rubrique que précédemment, lien &lt;em&gt;Identity providers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A priori, tout est en place côté serveur, reste à implémenter la couche MSAL côté client.&lt;/p&gt;

&lt;h1&gt;
  
  
  Côté client
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Package nuget &lt;em&gt;Microsoft Authentication Library (MSAL)&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Direction le nuget package manager. Installez &lt;em&gt;Microsoft.Identity.Client&lt;/em&gt; sur le projet commun et les projets des plateformes :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37qf4n6mkffcwwe6b7dd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37qf4n6mkffcwwe6b7dd.png" alt="Package nuget Microsoft.Identity.Client" width="491" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Préparation du projet commun
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Définition de quelques constantes
&lt;/h3&gt;

&lt;p&gt;Pour commencer, il est nécessaire de définir quelques constantes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tenantName&lt;/code&gt; : le nom de domaine que vous avez défini pour votre tenant Directory B2C&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tenantId&lt;/code&gt; : le même suffixé par &lt;code&gt;.onmicrosoft.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clientId&lt;/code&gt; : &lt;em&gt;Application (client) Id&lt;/em&gt;, vous trouverez cette information dans l’&lt;em&gt;overview&lt;/em&gt; de votre application sur le portail Azure : &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ize5pd6rh9s7d76mm32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ize5pd6rh9s7d76mm32.png" alt="ClientId dans l'overview de l'application" width="668" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;policySignin&lt;/code&gt; et &lt;code&gt;policyPassword&lt;/code&gt; : le nom des polices telles que vous les avez définies précédemment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iosKeychainSecurityGroup&lt;/code&gt; : identifiant du Bundle iOS que vous trouvez dans &lt;code&gt;Info.plist&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Constants&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tenantName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[your_tenant_name]"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[your_tenant_name].onmicrosoft.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;clientId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[your_application_client_id]"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;policySignin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"B2C_1_signin"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;policyPassword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"B2C_1_resetpassword"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;iosKeychainSecurityGroup&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[application iOS Bundle identifier]"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;authorityBase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tenantName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.b2clogin.com/tfp/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;RedirectUrl&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"msal&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://auth"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AuthorityBase&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authorityBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Scopes&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PolicySignin&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;policySignin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PolicyPassword&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;policyPassword&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;IosKeychainSecurityGroups&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;iosKeychainSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ClientId&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AuthoritySignin&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;authorityBase&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;policySignin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;AuthorityPasswordReset&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;authorityBase&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;policyPassword&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Le service d’authentification : &lt;code&gt;AuthenticationClient&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Dans le fichier &lt;code&gt;App.xaml.cs&lt;/code&gt;, définir les propriétés &lt;code&gt;AppUi&lt;/code&gt; (nécessaire pour l’implémentation dans Android) et &lt;code&gt;AuthenticationClient&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Identity.Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[...]&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IPublicClientApplication&lt;/span&gt; &lt;span class="n"&gt;AuthenticationClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;UIParent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le service d’authentification implémente &lt;code&gt;IPublicClientApplication&lt;/code&gt;, pour l’instancier, on utilise le builder &lt;code&gt;PublicClientApplicationBuilder&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;AuthenticationClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PublicClientApplicationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithIosKeychainSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IosKeychainSecurityGroups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithB2CAuthority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthoritySignin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithRedirectUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"msal&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://auth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration de l’application iOS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Info.plist
&lt;/h3&gt;

&lt;p&gt;Souvenez-vous côté serveur, vous avez configuré un &lt;em&gt;Redirect Uri&lt;/em&gt; sous la forme &lt;code&gt;msal[nom_libre]://auth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Il s’agit ici de renseigner cette valeur dans le fichier Info.plist du projet iOS. Cela se passe dans l’onglet &lt;em&gt;Advanced&lt;/em&gt;, rubrique &lt;em&gt;URL Types&lt;/em&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhdwhdv1z92knxaomexg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhdwhdv1z92knxaomexg.png" alt="Redirect Uri dans Info.plist" width="786" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Keychain dans Entitlements.plist
&lt;/h3&gt;

&lt;p&gt;Il est ensuite nécessaire d’enregistrer un Keychain dans &lt;em&gt;Entitlements.plist&lt;/em&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin9ysvi3omh70mhhiz8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin9ysvi3omh70mhhiz8v.png" alt="Keychain dans Entitlements.plist" width="781" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si cela ne s’est pas fait automatiquement, vérifiez bien que le fichier &lt;em&gt;Entitlements.plist&lt;/em&gt; est sélectionné comme &lt;em&gt;Custom Entitlements&lt;/em&gt; dans l’onglet &lt;em&gt;iOS Bundle Signing&lt;/em&gt; du projet iOS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdiii08zyrw6atredazn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdiii08zyrw6atredazn4.png" alt="Custom Entitlements" width="211" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Faîtes-le bien pour chacune de vos configurations de Build, sinon vous aurez une exception à l’exécution concernant le Keychain !&lt;/p&gt;

&lt;h3&gt;
  
  
  Surcharge de la méthode OpenUrl dans AppDelegate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Identity.Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[...]&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AppDelegate"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppDelegate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;global&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Xamarin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Forms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iOS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormsApplicationDelegate&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[...]&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;OpenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIApplication&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NSUrl&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NSDictionary&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AuthenticationContinuationHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetAuthenticationContinuationEventArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration l’application Android
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manifeste
&lt;/h3&gt;

&lt;p&gt;Comme pour iOS, le &lt;em&gt;Redirect Uri&lt;/em&gt; doit être déclaré dans le manifeste Android. Mais attention, il semblerait que seul &lt;em&gt;le Client Id&lt;/em&gt; (celui qui ressemble à &lt;code&gt;ead24568-9ecc-43f7-a5b4-ac62acd0a832&lt;/code&gt;) fonctionne.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;manifest&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt; &lt;span class="na"&gt;android:versionCode=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;android:versionName=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="na"&gt;package=&lt;/span&gt;&lt;span class="s"&gt;"com.companyname.adb2capp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;uses-sdk&lt;/span&gt; &lt;span class="na"&gt;android:minSdkVersion=&lt;/span&gt;&lt;span class="s"&gt;"21"&lt;/span&gt; &lt;span class="na"&gt;android:targetSdkVersion=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;application&lt;/span&gt; &lt;span class="na"&gt;android:label=&lt;/span&gt;&lt;span class="s"&gt;"ADB2CApp.Android"&lt;/span&gt; &lt;span class="na"&gt;android:theme=&lt;/span&gt;&lt;span class="s"&gt;"@style/MainTheme"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="c"&gt;&amp;lt;!-- MSAL REGISTRATION START --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"microsoft.identity.client.BrowserTabActivity"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"msal[your_app_Client_ID]"&lt;/span&gt; &lt;span class="na"&gt;android:host=&lt;/span&gt;&lt;span class="s"&gt;"auth"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- MSAL REGISTRATION END --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.ACCESS_NETWORK_STATE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Modifications dans &lt;em&gt;MainActivity&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;L’authentification MSAL nécessite :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;De passer l’activity à &lt;em&gt;App.UIParent&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;De surcharger &lt;em&gt;OnActivityResult&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Identity.Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[...]&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FormsAppCompatActivity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bundle&lt;/span&gt; &lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TabLayoutResource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tabbar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;ToolbarResource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Toolbar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Xamarin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Essentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;global&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Xamarin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Forms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Forms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;LoadApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// ADDED&lt;/span&gt;
        &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UIParent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// OVERRIDED&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnActivityResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;requestCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Intent&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnActivityResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;AuthenticationContinuationHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetAuthenticationContinuationEventArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implémentation dans le projet Xamarin.Forms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Création d’une page de Login
&lt;/h3&gt;

&lt;p&gt;On va faire super simple, créez une page &lt;code&gt;LoginPage.xaml&lt;/code&gt;, ajoutez-y un bouton &lt;code&gt;Login&lt;/code&gt; et abonnez-le à l’événement &lt;code&gt;Clicked&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage.Content&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"LoginButton"&lt;/span&gt;
        &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"LOGIN"&lt;/span&gt;
        &lt;span class="na"&gt;Clicked=&lt;/span&gt;&lt;span class="s"&gt;"LoginButton_Clicked"&lt;/span&gt;
        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage.Content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visuellement, difficile de faire plus basique :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fss8414waorscllf0mdoo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fss8414waorscllf0mdoo.png" alt="Login Page" width="270" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A l’apparition de la page, appelez &lt;code&gt;AcquireTokenSilentAsync&lt;/code&gt; pour rafraîchir le token d’authentification, au cas où un utilisateur soit déjà authentifié.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginPage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ContentPage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[...]&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnAppearing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Look for existing account&lt;/span&gt;
            &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAccount&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAccountsAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;AuthenticationResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AcquireTokenSilent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scopes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PushAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LogoutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Do nothing - the user isn't logged in&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnAppearing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Au clic sur le bouton, &lt;code&gt;AcquireTokenInteractive&lt;/code&gt; est utilisé pour ouvrir le navigateur de l’appareil et afficher la page de Signin.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si la connexion réussit le résultat de l’authentification est passé à la page &lt;code&gt;LogoutPage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Si l’utilisateur clique sur l’option “j’ai oublié mon mot de passe”, une exception particulière est interceptée pour lancer la procédure de récupération de mot de passe.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoginButton_Clicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AuthenticationResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AcquireTokenInteractive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scopes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectAccount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithParentActivityOrWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UIParent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PushAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LogoutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MsalException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AADB2C90118"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;OnForgotPassword&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PushAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LogoutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorCode&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"authentication_canceled"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;DisplayAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error has occurred"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Exception message: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Dismiss"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et voici le code pour la réinitialisation du mot de passe :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthenticationResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;OnForgotPassword&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AcquireTokenInteractive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scopes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectAccount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithParentActivityOrWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UIParent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithB2CAuthority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthorityPasswordReset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MsalException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Do nothing - ErrorCode will be displayed in OnLoginButtonClicked&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une fois la page créée, remplacez &lt;code&gt;MainPage&lt;/code&gt; dans &lt;code&gt;App&lt;/code&gt; par une &lt;code&gt;NavigationPage&lt;/code&gt; : &lt;code&gt;MainPage = new NavigationPage(new LoginPage());&lt;/code&gt; de façon à naviguer directement sur la &lt;code&gt;LoginPage&lt;/code&gt; à l’ouverture de l’application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Création d’une page de logout
&lt;/h3&gt;

&lt;p&gt;La page &lt;code&gt;LogoutPage&lt;/code&gt; est très similaire à la page de login à ceci près :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Son constructeur reçoit en paramètre le résultat de l’authentification&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;Label&lt;/code&gt; affiche les informations de base de l’utilisateur&lt;/li&gt;
&lt;li&gt;Le bouton &lt;code&gt;Login&lt;/code&gt; est remplacé par un bouton &lt;code&gt;Logout&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage.Content&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;StackLayout&lt;/span&gt; &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"UserInfoLabel"&lt;/span&gt;
                   &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"Large"&lt;/span&gt;
                   &lt;span class="na"&gt;HorizontalTextAlignment=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                   &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"LogoutButton"&lt;/span&gt;
                    &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"LOGOUT"&lt;/span&gt;
                    &lt;span class="na"&gt;Clicked=&lt;/span&gt;&lt;span class="s"&gt;"LogoutButton_Clicked"&lt;/span&gt;
                    &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                    &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/StackLayout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage.Content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commençons par récupérer l’&lt;code&gt;AuthenticationResult&lt;/code&gt; dans le constructeur de la page :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;AuthenticationResult&lt;/span&gt; &lt;span class="n"&gt;authenticationResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LogoutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthenticationResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    
    &lt;span class="n"&gt;authenticationResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Les informations utilisateur (Claims)
&lt;/h4&gt;

&lt;p&gt;Les informations concernant l’utilisateur, celles que vous avez sélectionnées parmi les &lt;code&gt;Claims&lt;/code&gt; lors de la création de votre &lt;code&gt;User Flow Signin&lt;/code&gt; sont codées dans la propriété &lt;code&gt;authenticationResult.IdToken&lt;/code&gt; sous la forme d’un &lt;strong&gt;&lt;a href="https://fr.wikipedia.org/wiki/JSON_Web_Token" rel="noopener noreferrer"&gt;token jwt&lt;/a&gt;.&lt;/strong&gt;Pour le décoder, vous aurez besoin d’installer le package nuget &lt;code&gt;System.IdentityModel.Tokens.Jwt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yrmudnx9dsvknbalmfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yrmudnx9dsvknbalmfg.png" alt="Package nuget JWT" width="354" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et voici le snippet pour le décoder :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authenticationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;JwtSecurityTokenHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;JwtSecurityToken&lt;/span&gt; &lt;span class="n"&gt;decodedToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JwtSecurityToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vous retrouverez toutes les informations dans une collection d’objet &lt;code&gt;Claim&lt;/code&gt;, notamment au travers les propriétés &lt;code&gt;Type&lt;/code&gt; et &lt;code&gt;Value&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnjjr0aifeop3eh02y8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnjjr0aifeop3eh02y8x.png" alt="Structure des données de Claim" width="621" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exemple, pour afficher le nom et la ville de l’utilisateur :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Noname"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Unknown city"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;UserInfoLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Welcome &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, from &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le code sera appelé de préférence dans le &lt;code&gt;OnAppearing&lt;/code&gt; de la page plutôt que dans son constructeur.&lt;/p&gt;

&lt;p&gt;Et voilà le résultat :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmy3x1crxisczj2x9cc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmy3x1crxisczj2x9cc5.png" alt="Page de Logout" width="270" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Déconnexion
&lt;/h3&gt;

&lt;p&gt;La déconnexion consiste principalement à retirer un compte de &lt;code&gt;AuthenticationClient&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LogoutButton_Clicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAccount&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAccountsAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAccountsAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PopAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  La page de Signin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6ydppnqt96n3ynglgmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6ydppnqt96n3ynglgmu.png" alt="Page de Signin" width="270" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Page de réinitialisation de mot de passe
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiss7zw6oaqw71v458bp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiss7zw6oaqw71v458bp.png" alt="Page de réinitialisation de mot de passe" width="270" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>xamarinforms</category>
      <category>azure</category>
      <category>authentification</category>
      <category>french</category>
    </item>
    <item>
      <title>Le Xamarin Community Toolkit</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Wed, 27 Jan 2021 16:58:25 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/le-xamarin-community-toolkit-24db</link>
      <guid>https://dev.to/sylvainmoingeon/le-xamarin-community-toolkit-24db</guid>
      <description>&lt;p&gt;Fruit de la collaboration entre l'équipe Xamarin et la communauté Open Source, le &lt;a href="https://github.com/xamarin/XamarinCommunityToolkit" rel="noopener noreferrer"&gt;Xamarin Community Toolkit&lt;/a&gt; est à &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt; ce que le sabre laser est au Jedi, c'est-à-dire son indispensable complément.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2grunihl66657hq5nh5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2grunihl66657hq5nh5k.png" width="300" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si j'en parle aujourd'hui, c'est parce que le toolkit est désormais disponible dans sa version stable mais aussi parce que c'est lui qui recevra toutes les nouveautés concernant Xamarin.Forms, cette plateforme n'étant plus vouée à évoluer après la version 5.0 et jusqu'à son absorption par &lt;a href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/" rel="noopener noreferrer"&gt;dotNet MAUI&lt;/a&gt;. Mais voyons cela plus en détails.&lt;/p&gt;




&lt;h2&gt;
  
  
  Xamarin.Forms 5.0
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt; est depuis peu disponible dans sa dernière version, et quand je dis dernière, c'est réellement la dernière. Ensuite, il n'y en aura plus ! Mais n'ayez crainte, il ne s'agit pas d'une disparition mais d'une mutation vers &lt;a href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/" rel="noopener noreferrer"&gt;Microsoft MAUI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Toujours est-il que dans un soucis de qualité, l'équipe Xamarin a décidé que cette dernière mouture serait avant tout axée sur la stabilité et l'amélioration des performances. Le choix a donc été fait de déplacer dans le Community Toolkit les fonctionnalités de Xamarin.Forms qui étaient restée au stade expérimental (comme les contrôles &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/views/expander" rel="noopener noreferrer"&gt;Expander&lt;/a&gt; ou &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/views/mediaelement" rel="noopener noreferrer"&gt;MediaElement&lt;/a&gt; par exemple) ou dont le développement n'était pas terminé.&lt;/p&gt;

&lt;p&gt;C'est donc au &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" rel="noopener noreferrer"&gt;Community Toolkit&lt;/a&gt; que revient la charge de toutes les nouveautés de Xamarin.Forms !&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vous comprenez donc bien que pour bénéficier des prochaines évolutions de Xamarin.Forms, il faudra vous tourner vers le toolkit !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mais ce fameux toolkit, que contient-il donc ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Le Xamarin Community Toolkit
&lt;/h2&gt;

&lt;p&gt;Je ne vais pas vous donner une liste exhaustive du contenu du toolkit, d'abord parce que tout est disponible dans &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" rel="noopener noreferrer"&gt;sa documentation&lt;/a&gt; et surtout il va continuer d'évoluer après l'écriture de cet article. Mais voici ce que vous y trouverez dans les grandes lignes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Behaviors
&lt;/h3&gt;

&lt;p&gt;Fonctionnalité peu connue (ou en tout cas trop peu utilisée) de Xamarin.Forms, les &lt;code&gt;Behaviors&lt;/code&gt; sont des bouts de code réutilisables qui ajoutent des comportements à vos composants XAML. Leur puissance est d'être modulable et de permettre d'ajouter ou de modifier des comportements à des contrôles XAML sans en modifier le code ni en créer de nouveaux.&lt;/p&gt;

&lt;p&gt;Par exemple, vous trouverez dans le toolkit un &lt;code&gt;EmailValidationBehavior&lt;/code&gt; qui, vous l'aurez compris, ajoute un comportement de validation d'email à vos champs de saisie.&lt;/p&gt;

&lt;p&gt;Dans un autre registre, il y a le toujours très utile &lt;code&gt;EventToCommandBehavior&lt;/code&gt; qui permet d'exécuter une commande de votre ViewModel à partir du déclenchement d'un événement du contrôle.&lt;/p&gt;

&lt;p&gt;Bref, les &lt;code&gt;Behaviors&lt;/code&gt; sont puissants et très diversifiés, vous en trouverez une grosse douzaine dans le toolkit !&lt;/p&gt;

&lt;h3&gt;
  
  
  Converters
&lt;/h3&gt;

&lt;p&gt;Les &lt;code&gt;Converters&lt;/code&gt; deviennent vite indispensables quand on souhaite suivre proprement l'architecture MVVM. Ils évitent bien des bidouilles inutiles dans vos ViewModels (genre créer artificiellement une propriété qui inverse la valeur booléenne d'une autre, vous voyez ce que je veux dire, hein, inutile de nier !).&lt;/p&gt;

&lt;p&gt;Le plus connu de tous est sans doute le &lt;code&gt;Converter&lt;/code&gt; d'inversion de booléen : vous avez une propriété &lt;code&gt;IsBusy&lt;/code&gt; dans votre ViewModel et vous voulez rendre un contrôle visible quand sa valeur est &lt;code&gt;false&lt;/code&gt;. Vous devez donc lier la propriété &lt;code&gt;IsVisible&lt;/code&gt; avec l'inverse de &lt;code&gt;IsBusy&lt;/code&gt; ! Vous avez le choix entre &lt;a href="https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4"&gt;faire le cradingue&lt;/a&gt; et ajouter une nouvelle propriété &lt;code&gt;IsNotBusy&lt;/code&gt; dans votre code (non mais quelle horreur) ou faire cela proprement avec un Converter.&lt;/p&gt;

&lt;p&gt;C'est une vingtaine de &lt;code&gt;Converters&lt;/code&gt; très utiles qui vous attendent dans le toolkit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Des tas de Views (contrôles XAML)
&lt;/h3&gt;

&lt;p&gt;AvatarView, BadgeView, CameraView, Expander, MediaElement... une douzaine de contrôles XAML prêts à l'emploi qui vous éviteront d'installer de multiples librairies tierces pas toujours maintenus dans le temps et finissent souvent par poser des problèmes de compatibilité. Ici, pas de soucis, c'est maintenu main dans la main par l'équipe Xamarin et la communauté.&lt;/p&gt;

&lt;h3&gt;
  
  
  Et d'autres choses encore
&lt;/h3&gt;

&lt;p&gt;Des helpers MVVM, des &lt;code&gt;Effects&lt;/code&gt; XAML (cousin des &lt;code&gt;Behaviors&lt;/code&gt; mais plus light), des extensions, pour en savoir plus, je vous invite à consulter &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" rel="noopener noreferrer"&gt;la documentation&lt;/a&gt; et les quelques ressources que je donne juste après, là, maintenant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ressources
&lt;/h2&gt;

&lt;p&gt;J'ai déjà donné le lien plusieurs fois mais si vous l'avez manqué, dirigez-vous vers la &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" rel="noopener noreferrer"&gt;documentation officielle du Xamarin Community Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Le dépôt GitHub : &lt;a href="https://github.com/xamarin/XamarinCommunityToolkit" rel="noopener noreferrer"&gt;https://github.com/xamarin/XamarinCommunityToolkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le dépôt de l'application d'exemples : &lt;a href="https://github.com/xamarin/XamarinCommunityToolkit/tree/main/samples" rel="noopener noreferrer"&gt;https://github.com/xamarin/XamarinCommunityToolkit/tree/main/samples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;L'article de blog de Gerald Versluis : &lt;a href="https://devblogs.microsoft.com/xamarin/xamarin-community-toolkit/" rel="noopener noreferrer"&gt;Xamarin Community Toolkit: A Must-Have Xamarin Library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les vidéos de présentation par deux stars du monde Xamarin (Gerald Versluis et Javier Suarez)&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/l09fzU0_N04"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/JmZbfr1590A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>xamarinforms</category>
      <category>xamarin</category>
      <category>communitytoolkit</category>
      <category>french</category>
    </item>
    <item>
      <title>Planet Xamarin</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Tue, 26 Jan 2021 16:53:14 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/planet-xamarin-b6l</link>
      <guid>https://dev.to/sylvainmoingeon/planet-xamarin-b6l</guid>
      <description>&lt;p&gt;Chercher des ressources concernant Xamarin.Forms est parfois un parcours du combattant. C'est tout éparpillé, il y a du bon, du moins bon, des choses pas franchement à jour.&lt;/p&gt;

&lt;p&gt;Et s'il existait un pays merveilleux dans lequel les meilleurs blogs étaient réunis ?&lt;/p&gt;

&lt;p&gt;Ce pays, ou plutôt cette planète, existe et s'appelle &lt;a href="https://www.planetxamarin.com/" rel="noopener noreferrer"&gt;Planet Xamarin&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;Il s'agit d'un agrégateur de blog, s'y abonner, c'est &lt;a href="https://www.planetxamarin.com/feed" rel="noopener noreferrer"&gt;s'abonner au gratin international des blogs Xamarin&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;Bon, c'est vrai, j'en fais des caisses, mais c'est aussi parce que depuis peu je fais partie de cette joyeuse communauté. Mon blog est désormais "Featured by Planet Xamarin".&lt;br&gt;
&lt;a href="https://www.planetxamarin.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38tdw43dhdti79vft53p.png" alt="Logo de Planet Xamarin" width="430" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>xamarinforms</category>
      <category>french</category>
    </item>
    <item>
      <title>Utiliser des images vectorielles SVG dans Xamarin.Forms</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Tue, 17 Nov 2020 14:55:28 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/utiliser-des-images-vectorielles-svg-dans-xamarin-forms-1obh</link>
      <guid>https://dev.to/sylvainmoingeon/utiliser-des-images-vectorielles-svg-dans-xamarin-forms-1obh</guid>
      <description>&lt;p&gt;Il est possible de longue date d'utiliser des bibliothèques tierces telles que &lt;a href="https://github.com/mono/SkiaSharp" rel="noopener noreferrer"&gt;Skiasharp&lt;/a&gt; pour afficher des images vectorielles dans &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt;. Mais quand on est soucieux de la légèreté et des performances de son application, il est souvent préférable de se limiter aux fonctionnalités proposées par défaut par notre plateforme de développement.&lt;/p&gt;

&lt;p&gt;Depuis l'apparition des &lt;code&gt;Path&lt;/code&gt; avec la version 4.8, Xamarin.Forms est en mesure d'utiliser d'afficher des images vectorielles sans l'aide d'aucun autre package !&lt;/p&gt;

&lt;p&gt;Je vous montre comment ça se path... euh... passe ?&lt;/p&gt;




&lt;h2&gt;
  
  
  Convertir le contenu d'un SVG en Path
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Les fonctionnalités utilisées dans cet article nécessitent Xamarin.Forms 5.0 ou au minimum Xamarin.Forms 4.8 avec le flag expérimental : &lt;code&gt;Device.SetFlags(new string[] { "Shapes_Experimental" });&lt;/code&gt; dans le constructeur de la classe &lt;code&gt;App&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alors oui, je vous ai un tout petit peu menti. On ne peut toujours pas incorporer un fichier svg et l'utiliser comme ressource. Mais un fichier svg n'étant que du texte balisé en XML, il est très facile d'en extraire le contenu et d'en faire une forme dans Xamarin.Form.&lt;/p&gt;

&lt;p&gt;Nous prendrons deux exemples : un cas simple où l'image est constituée d'un seul chemin et un cas plus complexe où l'image est composée de plusieurs chemins.&lt;/p&gt;

&lt;p&gt;Nous verrons ensuite comment rendre une image vectorielle réutilisable facilement dans le code sans duplication à l'aide des &lt;code&gt;Styles&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image simple composée d'un seul chemin
&lt;/h3&gt;

&lt;p&gt;Nous allons partir de ce svg très simple représentant un avion :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F39%2Fairport.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F39%2Fairport.svg" alt="Une image vectorielle simple" width="64" height="64"&gt;&lt;/a&gt;Une image vectorielle simple&lt;/p&gt;

&lt;p&gt;Si vous ouvrez le fichier avec un éditeur de texte, vous y trouverez ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;enable-background=&lt;/span&gt;&lt;span class="s"&gt;"new 0 0 64 64"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.1"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 64 64"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M15,6.8182L15,8.5l-6.5-1 l-0.3182,4.7727L11,14v1l-3.5-0.6818L4,15v-1l2.8182-1.7273L6.5,7.5L0,8.5V6.8182L6.5,4.5v-3c0,0,0-1.5,1-1.5s1,1.5,1,1.5v2.8182 L15,6.8182z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce qui nous intéresse ici, c'est la balise &lt;code&gt;path&lt;/code&gt; et en particulier son attribut &lt;code&gt;d&lt;/code&gt; contenant le tracé du dessin. Nous allons simplement copier le contenu de &lt;code&gt;d&lt;/code&gt; dans la propriété &lt;code&gt;Data&lt;/code&gt; d'un &lt;code&gt;Path&lt;/code&gt; Xamarin.Forms. Comme ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Path&lt;/span&gt; &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"Uniform"&lt;/span&gt;
      &lt;span class="na"&gt;Data=&lt;/span&gt;&lt;span class="s"&gt;"M15,6.8182L15,8.5l-6.5-1 l-0.3182,4.7727L11,14v1l-3.5-0.6818L4,15v-1l2.8182-1.7273L6.5,7.5L0,8.5V6.8182L6.5,4.5v-3c0,0,0-1.5,1-1.5s1,1.5,1,1.5v2.8182 L15,6.8182z"&lt;/span&gt;
      &lt;span class="na"&gt;Fill=&lt;/span&gt;&lt;span class="s"&gt;"Black"&lt;/span&gt;
      &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La propriété &lt;code&gt;Aspect&lt;/code&gt; avec la valeur &lt;code&gt;Uniform&lt;/code&gt; conserve les proportions de l'image. La propriété &lt;code&gt;Fill&lt;/code&gt; indique la couleur de remplissage de la forme.&lt;/p&gt;

&lt;p&gt;Sans entrer trop loin dans les détails, il est très facile de manipuler l'image, par exemple en modifier le contour, le remplissage ou lui appliquer des transformations.&lt;/p&gt;

&lt;p&gt;La même image vectorielle agrandie x2, pivotée de 45° avec un fond vert et un contour pointillé noir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Path&lt;/span&gt; &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"Uniform"&lt;/span&gt;
      &lt;span class="na"&gt;Data=&lt;/span&gt;&lt;span class="s"&gt;"M15,6.8182L15,8.5l-6.5-1 l-0.3182,4.7727L11,14v1l-3.5-0.6818L4,15v-1l2.8182-1.7273L6.5,7.5L0,8.5V6.8182L6.5,4.5v-3c0,0,0-1.5,1-1.5s1,1.5,1,1.5v2.8182 L15,6.8182z"&lt;/span&gt;            
      &lt;span class="na"&gt;Fill=&lt;/span&gt;&lt;span class="s"&gt;"Green"&lt;/span&gt;
      &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
      &lt;span class="na"&gt;Rotation=&lt;/span&gt;&lt;span class="s"&gt;"45"&lt;/span&gt;
      &lt;span class="na"&gt;Stroke=&lt;/span&gt;&lt;span class="s"&gt;"Black"&lt;/span&gt;
      &lt;span class="na"&gt;Scale=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
      &lt;span class="na"&gt;StrokeDashArray=&lt;/span&gt;&lt;span class="s"&gt;"2 1"&lt;/span&gt;
      &lt;span class="na"&gt;StrokeThickness=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le résultat sous Android :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmob6afefgtq42xn1pw67.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmob6afefgtq42xn1pw67.png" alt="Une image vectorielle, affichée et manipulée dans Xamarin.Forms" width="355" height="295"&gt;&lt;/a&gt;Une image vectorielle, affichée et manipulée dans Xamarin.Forms&lt;/p&gt;

&lt;h3&gt;
  
  
  Image complexe composée de plusieurs chemins
&lt;/h3&gt;

&lt;p&gt;Malheureusement, les images vectorielles ne sont pas toujours aussi simples et sont parfois composées de plusieurs chemins. Par exemple celle-ci :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F39%2Fcar.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F39%2Fcar.svg" width="64" height="64"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"512"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"512"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 512 512"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M104,320a24,24,0,1,0,24,24A24.028,24.028,0,0,0,104,320Zm0,32a8,8,0,1,1,8-8A8.009,8.009,0,0,1,104,352Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M408,320a24,24,0,1,0,24,24A24.028,24.028,0,0,0,408,320Zm0,32a8,8,0,1,1,8-8A8.009,8.009,0,0,1,408,352Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M488,304V256a48.051,48.051,0,0,0-48-48H373.54l-22-58.68A43.052,43.052,0,0,0,310.7,120H160a43.044,43.044,0,0,0-40.72,28.97L90.96,208H72a48.051,48.051,0,0,0-48,48v48A16.021,16.021,0,0,0,8,320v16a16.021,16.021,0,0,0,16,16H48.58a55.994,55.994,0,0,0,110.84,0H352.58a55.994,55.994,0,0,0,110.84,0H488a16.021,16.021,0,0,0,16-16V320A16.021,16.021,0,0,0,488,304ZM356.46,208H256V160h82.46ZM40,264H56v8H40Zm8.58,72H24V320H53.41A55.5,55.5,0,0,0,48.58,336ZM104,384a40,40,0,1,1,40-40A40.04,40.04,0,0,1,104,384Zm248.58-48H159.42a55.5,55.5,0,0,0-4.83-16H357.41A55.5,55.5,0,0,0,352.58,336ZM408,384a40,40,0,1,1,40-40A40.04,40.04,0,0,1,408,384Zm40-80a7.337,7.337,0,0,0-.81.05,55.871,55.871,0,0,0-78.37-.01A7.383,7.383,0,0,0,368,304H144a7.383,7.383,0,0,0-.82.04,55.871,55.871,0,0,0-78.37.01A7.337,7.337,0,0,0,64,304H40V288H64a8,8,0,0,0,8-8V256a8,8,0,0,0-8-8H41.01A32.058,32.058,0,0,1,72,224H96a8,8,0,0,0,7.21-4.54l30.71-64a7.5,7.5,0,0,0,.37-.93A27.079,27.079,0,0,1,160,136H310.7a27.027,27.027,0,0,1,19.22,8H160a8,8,0,0,0-8,8v64a8,8,0,0,0,8,8H440a32.058,32.058,0,0,1,30.99,24H448a8,8,0,0,0-8,8v24a8,8,0,0,0,8,8h24v16ZM240,160v48H168V160ZM472,264v8H456v-8Zm16,72H463.42a55.5,55.5,0,0,0-4.83-16H488Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M256,248h40a8,8,0,0,0,0-16H256a8,8,0,0,0,0,16Z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Malheur ! l'image contient plusieurs chemins mais notre objet &lt;code&gt;Path&lt;/code&gt; n'a qu'une seule propriété &lt;code&gt;Data&lt;/code&gt; ! Comment faire ?&lt;/p&gt;

&lt;p&gt;Heureusement, la propriété &lt;code&gt;Data&lt;/code&gt; de notre &lt;code&gt;Path&lt;/code&gt; peut contenir bien plus qu'une simple chaîne de caractère, notamment un groupe de géométrie. Nous allons donc créer un &lt;code&gt;GeometryGroup&lt;/code&gt; contenant des &lt;code&gt;PathGeometry&lt;/code&gt; dont nous renseigneront la propriété &lt;code&gt;Figures&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Chaque path de notre svg alimente ainsi les propriétés &lt;code&gt;Figures&lt;/code&gt; de nos &lt;code&gt;PathGeometry&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Path&lt;/span&gt; &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"Uniform"&lt;/span&gt;
      &lt;span class="na"&gt;Fill=&lt;/span&gt;&lt;span class="s"&gt;"Black"&lt;/span&gt;
      &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Path.Data&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;GeometryGroup&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M104,320a24,24,0,1,0,24,24A24.028,24.028,0,0,0,104,320Zm0,32a8,8,0,1,1,8-8A8.009,8.009,0,0,1,104,352Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M408,320a24,24,0,1,0,24,24A24.028,24.028,0,0,0,408,320Zm0,32a8,8,0,1,1,8-8A8.009,8.009,0,0,1,408,352Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M488,304V256a48.051,48.051,0,0,0-48-48H373.54l-22-58.68A43.052,43.052,0,0,0,310.7,120H160a43.044,43.044,0,0,0-40.72,28.97L90.96,208H72a48.051,48.051,0,0,0-48,48v48A16.021,16.021,0,0,0,8,320v16a16.021,16.021,0,0,0,16,16H48.58a55.994,55.994,0,0,0,110.84,0H352.58a55.994,55.994,0,0,0,110.84,0H488a16.021,16.021,0,0,0,16-16V320A16.021,16.021,0,0,0,488,304ZM356.46,208H256V160h82.46ZM40,264H56v8H40Zm8.58,72H24V320H53.41A55.5,55.5,0,0,0,48.58,336ZM104,384a40,40,0,1,1,40-40A40.04,40.04,0,0,1,104,384Zm248.58-48H159.42a55.5,55.5,0,0,0-4.83-16H357.41A55.5,55.5,0,0,0,352.58,336ZM408,384a40,40,0,1,1,40-40A40.04,40.04,0,0,1,408,384Zm40-80a7.337,7.337,0,0,0-.81.05,55.871,55.871,0,0,0-78.37-.01A7.383,7.383,0,0,0,368,304H144a7.383,7.383,0,0,0-.82.04,55.871,55.871,0,0,0-78.37.01A7.337,7.337,0,0,0,64,304H40V288H64a8,8,0,0,0,8-8V256a8,8,0,0,0-8-8H41.01A32.058,32.058,0,0,1,72,224H96a8,8,0,0,0,7.21-4.54l30.71-64a7.5,7.5,0,0,0,.37-.93A27.079,27.079,0,0,1,160,136H310.7a27.027,27.027,0,0,1,19.22,8H160a8,8,0,0,0-8,8v64a8,8,0,0,0,8,8H440a32.058,32.058,0,0,1,30.99,24H448a8,8,0,0,0-8,8v24a8,8,0,0,0,8,8h24v16ZM240,160v48H168V160ZM472,264v8H456v-8Zm16,72H463.42a55.5,55.5,0,0,0-4.83-16H488Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M256,248h40a8,8,0,0,0,0-16H256a8,8,0,0,0,0,16Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/GeometryGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Path.Data&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Path&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et hop :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6l8z3h6yjdaeyc9jf8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6l8z3h6yjdaeyc9jf8k.png" width="353" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nous voilà capable d'afficher des images vectorielles simples ou complexes directement dans Xamarin.Forms. Mais pour l'instant, cela reste fastidieux : pour utiliser une même image à plusieurs endroits, il reste nécessaire de copier-coller tout le contenu du &lt;code&gt;Path&lt;/code&gt; à chaque fois. &lt;/p&gt;

&lt;p&gt;Voyons comment résoudre ce problème avec Style !&lt;/p&gt;

&lt;h2&gt;
  
  
  Créer une image vectorielle réutilisable grâce aux styles
&lt;/h2&gt;

&lt;p&gt;Nous allons créer un &lt;code&gt;ResourceDictionary&lt;/code&gt; pour y placer des &lt;code&gt;Styles&lt;/code&gt; définissant le tracé de chaque image vectorielle.&lt;/p&gt;

&lt;p&gt;Toutes nos images SVG auront des propriétés communes, nous allons donc commencer par créer un style de base :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Style&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"BaseSVGStyle"&lt;/span&gt; &lt;span class="na"&gt;TargetType=&lt;/span&gt;&lt;span class="s"&gt;"Path"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"Aspect"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"Uniform"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"Fill"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"Black"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous allons ensuite créer un style par image héritant de &lt;code&gt;BaseSVGStyle&lt;/code&gt; à l'aide de la propriété &lt;code&gt;BasedOn&lt;/code&gt;. Voici un exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Style&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"PlaneSVG"&lt;/span&gt;
        &lt;span class="na"&gt;BasedOn=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource BaseSVGStyle}"&lt;/span&gt;
        &lt;span class="na"&gt;TargetType=&lt;/span&gt;&lt;span class="s"&gt;"Path"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"Data"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"m430.37 48.711c29.15-29.153-11.17-67.267-39.96-38.479l-106.3 106.3-243.52-64.372-40.59 40.595 200.71 105.71-81.18 81.17-63.104-7.75-32.106 32.11 71.595 37.64 37.645 71.6 32.1-32.11-7.38-62.74 81.18-81.17 103.43 199.39 40.6-40.6-61.63-238.58 108.51-108.71z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"HeightRequest"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;Style&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"CarSVG"&lt;/span&gt;
        &lt;span class="na"&gt;BasedOn=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource BaseSVGStyle}"&lt;/span&gt;
        &lt;span class="na"&gt;TargetType=&lt;/span&gt;&lt;span class="s"&gt;"Path"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"HeightRequest"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"Data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Setter.Value&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;GeometryGroup&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M104,320a24,24,0,1,0,24,24A24.028,24.028,0,0,0,104,320Zm0,32a8,8,0,1,1,8-8A8.009,8.009,0,0,1,104,352Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M408,320a24,24,0,1,0,24,24A24.028,24.028,0,0,0,408,320Zm0,32a8,8,0,1,1,8-8A8.009,8.009,0,0,1,408,352Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M488,304V256a48.051,48.051,0,0,0-48-48H373.54l-22-58.68A43.052,43.052,0,0,0,310.7,120H160a43.044,43.044,0,0,0-40.72,28.97L90.96,208H72a48.051,48.051,0,0,0-48,48v48A16.021,16.021,0,0,0,8,320v16a16.021,16.021,0,0,0,16,16H48.58a55.994,55.994,0,0,0,110.84,0H352.58a55.994,55.994,0,0,0,110.84,0H488a16.021,16.021,0,0,0,16-16V320A16.021,16.021,0,0,0,488,304ZM356.46,208H256V160h82.46ZM40,264H56v8H40Zm8.58,72H24V320H53.41A55.5,55.5,0,0,0,48.58,336ZM104,384a40,40,0,1,1,40-40A40.04,40.04,0,0,1,104,384Zm248.58-48H159.42a55.5,55.5,0,0,0-4.83-16H357.41A55.5,55.5,0,0,0,352.58,336ZM408,384a40,40,0,1,1,40-40A40.04,40.04,0,0,1,408,384Zm40-80a7.337,7.337,0,0,0-.81.05,55.871,55.871,0,0,0-78.37-.01A7.383,7.383,0,0,0,368,304H144a7.383,7.383,0,0,0-.82.04,55.871,55.871,0,0,0-78.37.01A7.337,7.337,0,0,0,64,304H40V288H64a8,8,0,0,0,8-8V256a8,8,0,0,0-8-8H41.01A32.058,32.058,0,0,1,72,224H96a8,8,0,0,0,7.21-4.54l30.71-64a7.5,7.5,0,0,0,.37-.93A27.079,27.079,0,0,1,160,136H310.7a27.027,27.027,0,0,1,19.22,8H160a8,8,0,0,0-8,8v64a8,8,0,0,0,8,8H440a32.058,32.058,0,0,1,30.99,24H448a8,8,0,0,0-8,8v24a8,8,0,0,0,8,8h24v16ZM240,160v48H168V160ZM472,264v8H456v-8Zm16,72H463.42a55.5,55.5,0,0,0-4.83-16H488Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;PathGeometry&lt;/span&gt; &lt;span class="na"&gt;Figures=&lt;/span&gt;&lt;span class="s"&gt;"M256,248h40a8,8,0,0,0,0-16H256a8,8,0,0,0,0,16Z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/GeometryGroup&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Setter.Value&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Setter&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les styles consistent donc principalement à définir la valeur de la propriété &lt;code&gt;Data&lt;/code&gt; de nos &lt;code&gt;Path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour les utiliser dans nos &lt;code&gt;Views&lt;/code&gt;, nous devons d'abord déclarer le dictionnaire de ressource. Soit dans les ressources de la page, soit dans &lt;code&gt;App.xaml&lt;/code&gt; si l'on souhaite accéder aux images partout dans le projet (&lt;em&gt;PathesStyles&lt;/em&gt; est le nom de mon dictionnaire de ressource).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage.Resources&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ResourceDictionary&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pathes:PathesStyles&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage.Resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, dans la contenu de la page, c'est simple comme &lt;em&gt;Bonjour&lt;/em&gt; , on applique le style souhaité à notre path :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Path&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource CarSVG}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Il est toujours possible, bien entendu, de surcharger les valeurs proposées par le style. Pour afficher l'image en rouge par exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Path&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource CarSVG}"&lt;/span&gt; &lt;span class="na"&gt;Fill=&lt;/span&gt;&lt;span class="s"&gt;"Red"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54uktm0gge437qvjy0h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54uktm0gge437qvjy0h8.png" alt="Un florilège d'images vectorielles dans Xamarin.Forms" width="608" height="1250"&gt;&lt;/a&gt;Un florilège d'images vectorielles dans Xamarin.Forms&lt;/p&gt;

&lt;p&gt;Pour la démo, les valeurs sont en dur dans le code, mais bien entendu toutes les propriétés sont &lt;code&gt;Bindables&lt;/code&gt; pour en modifier les valeurs à l'exécution. Votre créativité fera le reste !&lt;/p&gt;

&lt;h2&gt;
  
  
  Projet GitHub
&lt;/h2&gt;

&lt;p&gt;Retrouvez la &lt;a href="https://github.com/SylvainMoingeon/SVGDemo" rel="noopener noreferrer"&gt;démonstration de l'utilisation des Path Xamarin.Forms pour afficher des images vectorielles&lt;/a&gt; dans mon dépôt GitHub.&lt;/p&gt;

&lt;p&gt;Pensez bien à liker et partager pour que ce soit utile au plus grand nombre !&lt;/p&gt;

</description>
      <category>svg</category>
      <category>xamarinforms</category>
      <category>xaml</category>
      <category>french</category>
    </item>
    <item>
      <title>Créer un contrôle réutilisable 100% Xamarin.Forms, partie 2</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Tue, 10 Nov 2020 17:22:28 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-2-30ec</link>
      <guid>https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-2-30ec</guid>
      <description>&lt;p&gt;Vous apprendrez notamment à créer des propriétés bindables (&lt;code&gt;BindableProperty&lt;/code&gt;) et à utiliser les &lt;code&gt;Converters&lt;/code&gt;. &lt;/p&gt;

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




&lt;h2&gt;
  
  
  Mais où va-t-on ?
&lt;/h2&gt;

&lt;p&gt;Commençons par un petit rappel, voici une démonstration animée de l'image circulaire en situation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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, &lt;strong&gt;un seul code pour générer deux applications natives&lt;/strong&gt;. C'est la beauté de Xamarin.Forms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On y voit : &lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/37/gallery/demo-circleimage.gif" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feusiw8xzl3s2rgoqbx2p.gif" alt="Screenshot animé sous Android" width="350" height="726"&gt;&lt;/a&gt;L'image circulaire en situation sous Android&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/media/posts/37/gallery/circleimage-ios-screenshot.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1bxhssbtpzynftmy9i6.png" alt="Screenshot sous iOS" width="768" height="1663"&gt;&lt;/a&gt;Copie d'écran sous iOS&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Création du contrôle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Composons
&lt;/h3&gt;

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

&lt;p&gt;Nous avons donc une base avec une image circulaire simple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monimage.png"&lt;/span&gt; &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"64,64"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et une image circulaire avancée, composée à partir de la précédente :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder.png"&lt;/span&gt; &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"64,64"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monimage.png"&lt;/span&gt; &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"64,64"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Ellipse&lt;/span&gt; &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
             &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
             &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
             &lt;span class="na"&gt;Stroke=&lt;/span&gt;&lt;span class="s"&gt;"Yellow"&lt;/span&gt;
             &lt;span class="na"&gt;StrokeThickness=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
             &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;
             &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;
             &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

     &lt;span class="nt"&gt;&amp;lt;ActivityIndicator&lt;/span&gt; &lt;span class="na"&gt;IsRunning=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Source={x:Reference monImage}, Path=IsLoading}"&lt;/span&gt;
                        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 ?&lt;/p&gt;

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

&lt;p&gt;Quelque chose comme ça :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder"&lt;/span&gt;
                         &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder.png"&lt;/span&gt;
                         &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monImage"&lt;/span&gt;
                         &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monImage.png"&lt;/span&gt;
                         &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    [...] Le reste du contrôle avec la bordure et l'activityIndicator
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;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é &lt;code&gt;ImageSize&lt;/code&gt; bindable de façon à pouvoir écrire quelque chose comme :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder"&lt;/span&gt;
                     &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"{Binding MonImage}"&lt;/span&gt;
                     &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"{Binding MyImageSize}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La propriété &lt;code&gt;Source&lt;/code&gt; héritant du contrôle Image de base, elle est déjà bindable.&lt;/p&gt;

&lt;p&gt;Si ceci vous parait obscur, relisez mon article sur l'&lt;a href="https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g"&gt;architecture MVVM&lt;/a&gt; !&lt;/p&gt;

&lt;h3&gt;
  
  
  Créer un nouveau contrôle
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zlsysrn3hs7pgbsc2pp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zlsysrn3hs7pgbsc2pp.png" alt="Copie d'écran pour illustrer l'ajout d'un ContentView dans Xamarin.Forms" width="800" height="651"&gt;&lt;/a&gt;Ajoutez un ContentView nommé CircleImage&lt;/p&gt;

&lt;p&gt;Par défaut, votre &lt;code&gt;ContentView&lt;/code&gt; est de type... &lt;code&gt;ContentView&lt;/code&gt;. Ça va de soi mais je le précise pour les deux qui dorment au fond vers le radiateur.&lt;/p&gt;

&lt;p&gt;Nous pourrions ajouter notre &lt;code&gt;Image&lt;/code&gt; comme contenu du &lt;code&gt;ContentView&lt;/code&gt;, 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 &lt;code&gt;ContentView&lt;/code&gt; par le type &lt;code&gt;Image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Attention ! Pensez bien à le faire à la fois dans le XAML...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Remplacer --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ContentView&lt;/span&gt; &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"CircleImageDemo.CircleImage.CircleImage"&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="err"&gt;Par&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"CircleImageDemo.CircleImage.CircleImage"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...et dans le code-behind C# !&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Remplacer&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CircleImage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ContentView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Par&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CircleImage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;ImageSize&lt;/code&gt;. Commençons par ceci.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposer des propriétés bindables depuis l'extérieur
&lt;/h3&gt;

&lt;p&gt;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 &lt;code&gt;BindableProperty&lt;/code&gt; au lieu d'un champs privé comme cela se fait habituellement.&lt;/p&gt;

&lt;p&gt;Je vous vois froncer les sourcils, alors un exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#region ImageSize Bindable property
&lt;/span&gt;&lt;span class="c1"&gt;// Bindable property&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;BindableProperty&lt;/span&gt; &lt;span class="n"&gt;ImageSizeProperty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;BindableProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="n"&gt;propertyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ImageSize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;declaringType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CircleImage&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;defaultBindingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BindingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneWay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;propertyChanged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bindable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Gets or sets value of this BindableProperty&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;ImageSize&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ImageSizeProperty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ImageSizeProperty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;#endregion
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous avons la propriété &lt;code&gt;ImageSize&lt;/code&gt; qui sera exposée et sur laquelle on viendra "binder" dans le XAML.&lt;/p&gt;

&lt;p&gt;La gestion du binding proprement dit est laissée à la &lt;code&gt;BindableProperty&lt;/code&gt; &lt;code&gt;ImageSizeProperty&lt;/code&gt; instanciée par la méthode statique  &lt;code&gt;BindableProperty.Create&lt;/code&gt; avec les arguments suivants : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;propertyName&lt;/strong&gt; : nom de la propriété exposée, celle qui recevra les valeurs via Binding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;declaringType&lt;/strong&gt; : type de la classe portant la propriété, ici CircleImage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;returnType&lt;/strong&gt; : type retourné par la propriété&lt;/li&gt;
&lt;li&gt;defaultValue : valeur par défaut&lt;/li&gt;
&lt;li&gt;defaultBindingMode : définit le sens du binding par défaut&lt;/li&gt;
&lt;li&gt;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 &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/xamarin-forms/xaml/bindable-properties#coerce-value-callbacks" rel="noopener noreferrer"&gt;lire la documentation&lt;/a&gt; !&lt;/li&gt;
&lt;li&gt;propertyChanged : callback appelé quand la valeur du binding vient de changer&lt;/li&gt;
&lt;li&gt;propertyChanging : idem quand la valeur est en train de changer&lt;/li&gt;
&lt;li&gt;defaultValueCreator : permet de définir une valeur par défaut à partir d'une &lt;code&gt;Func&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seuls les trois premiers arguments sont obligatoires.&lt;/p&gt;

&lt;p&gt;Tout ceci se place bien entendu dans le code-behind de la &lt;code&gt;View&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nous avons désormais une propriété &lt;code&gt;ImageSize&lt;/code&gt; mais comment allons-nous l'utiliser en interne dans notre &lt;code&gt;CircleImage&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;propertyChanged&lt;/code&gt; ou &lt;code&gt;propertyChanging&lt;/code&gt; de la BindableProperty qui offrent plus de souplesse et de liberté que XAML.&lt;/p&gt;

&lt;p&gt;Je vais vous présenter les deux mais, bien entendu, dans le &lt;a href="https://github.com/SylvainMoingeon/CircleImageDemo" rel="noopener noreferrer"&gt;projet GitHub&lt;/a&gt; je n'ai pu en laisser qu'un !&lt;/p&gt;

&lt;h3&gt;
  
  
  Lier les propriétés bindables au XAML du contrôle
&lt;/h3&gt;

&lt;p&gt;Partons de notre &lt;code&gt;CircleImage&lt;/code&gt; de base.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"CircleImageDemo.CircleImage.CircleImage"&lt;/span&gt;
       &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://xamarin.com/schemas/2014/forms"&lt;/span&gt;
       &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
       &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;
       &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                         &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"64,64"&lt;/span&gt;
                         &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Sauf que là, comme ça, ça ne fonctionnera pas. Il est nécessaire de préciser à notre &lt;code&gt;View&lt;/code&gt; que son contexte de binding est... elle-même !&lt;/p&gt;

&lt;p&gt;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 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"this"&lt;/span&gt; &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On peut désormais binder &lt;code&gt;ImageSize&lt;/code&gt; aux propriétés de l'&lt;code&gt;Image&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"this"&lt;/span&gt;
       &lt;span class="err"&gt;[...]&lt;/span&gt;
       &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt;
       &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize}"&lt;/span&gt;
       &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize}"&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    [...]
&lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ça y est, nous avons une image carrée de côté &lt;code&gt;ImageSize&lt;/code&gt;. &lt;/p&gt;

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

&lt;p&gt;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 &lt;code&gt;Converter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;IValueConverter&lt;/code&gt;, 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 &lt;code&gt;DividerConverter&lt;/code&gt; !&lt;/p&gt;

&lt;p&gt;Pour rendre le converter plus versatile, celui-ci expose une propriété &lt;code&gt;Divider&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;La méthode &lt;code&gt;Convert()&lt;/code&gt; divise la valeur reçue de l'extérieur, la méthode &lt;code&gt;ConvertBack()&lt;/code&gt; 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 !)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DoubleDividerConverter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IValueConverter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;      
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Divider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Divise la valeur reçue par Divider&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;targetType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;doubleToDivide&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;doubleToDivide&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;Divider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt; should be of type double"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Multiplie la valeur renvoyée par Divider&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nf"&gt;ConvertBack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;targetType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt; &lt;span class="n"&gt;culture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;doubleToMultiply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;doubleToMultiply&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Divider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt; should be of type double"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reste à utiliser le Converter dans le XAML :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le déclarer comme ressource statique dans la &lt;code&gt;View&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;L'ajouter aux Bindings&lt;/li&gt;
&lt;li&gt;Préciser à l'&lt;code&gt;EllipseGeometry&lt;/code&gt; son contexte de Binding.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"this"&lt;/span&gt;
         &lt;span class="err"&gt;[...]&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image.Resources&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ResourceDictionary&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;local:DoubleDividerConverter&lt;/span&gt; 
                    &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"DoubleDividerConverter"&lt;/span&gt; 
                    &lt;span class="na"&gt;Divider=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ResourceDictionary&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image.Resources&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize, Converter={StaticResource DoubleDividerConverter}}"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize, Converter={StaticResource DoubleDividerConverter}}"&lt;/span&gt;
                         &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"??? Center attend un type Point, je vous laisse réfléchir quelques instants"&lt;/span&gt;
                         &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Reste à la positionner via la propriété &lt;code&gt;Center&lt;/code&gt;. Celle-ci attend une valeur de type &lt;code&gt;Point&lt;/code&gt;. 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.&lt;/p&gt;

&lt;p&gt;Vous trouverez tout le code dans le &lt;a href="https://github.com/SylvainMoingeon/CircleImageDemo" rel="noopener noreferrer"&gt;dépôt GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Gérer la propriété bindable dans le code behind
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Il suffit de placer notre code dans l'événement &lt;code&gt;propertyChanged&lt;/code&gt; de la &lt;code&gt;BindableProperty&lt;/code&gt; : 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.&lt;/p&gt;

&lt;p&gt;Et c'est tout !&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;BindableProperty&lt;/span&gt; &lt;span class="n"&gt;ImageSizeProperty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;BindableProperty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="n"&gt;propertyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ImageSize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;declaringType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CircleImage&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;defaultBindingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BindingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneWay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;propertyChanged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bindable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bindable&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;CircleImage&lt;/span&gt; &lt;span class="n"&gt;circleImage&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imageSize&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="n"&gt;EllipseGeometry&lt;/span&gt; &lt;span class="n"&gt;ellipseGeometry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EllipseGeometry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
             &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="n"&gt;Center&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="n"&gt;RadiusX&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;RadiusY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;
             &lt;span class="p"&gt;};&lt;/span&gt;

             &lt;span class="n"&gt;circleImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HeightRequest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="n"&gt;circleImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WidthRequest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="n"&gt;circleImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ellipseGeometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 !&lt;/p&gt;

&lt;p&gt;Il y a également un risque accru de &lt;a href="https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4"&gt;monstre spaghetti et de crotte de nez dans le code&lt;/a&gt;. Un développeur débutant ou peu consciencieux, aura vite fait de mélanger tout et n'importe quoi dans &lt;code&gt;propertyChanged&lt;/code&gt;. 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 &lt;code&gt;Converter&lt;/code&gt; 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.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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 !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Appliquons le même principe pour créer un contrôle avancé
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;Je vous laisse faire, c'est exactement comme tout à l'heure, avec une &lt;code&gt;Grid&lt;/code&gt; à la place de l'&lt;code&gt;Image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A l'intérieur de notre Grid, nous aurons donc : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deux itérations de notre &lt;code&gt;CircleImage&lt;/code&gt;, l'une pour l'image, l'autre pour l'image de substitution&lt;/li&gt;
&lt;li&gt;Une &lt;code&gt;Ellipse&lt;/code&gt; pour la bordure&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;ActivityIndicator&lt;/code&gt; pour l'indicateur de chargement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans le code-behind, nous définirons quelques &lt;code&gt;BindablePropertie&lt;/code&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MainImageSource : source de l'image principale&lt;/li&gt;
&lt;li&gt;PlaceholderImageSource : source du placeholder&lt;/li&gt;
&lt;li&gt;ImageSize : taille de l'image&lt;/li&gt;
&lt;li&gt;IsBorderVisible : la bordure est-elle visible ?&lt;/li&gt;
&lt;li&gt;BorderColor : couleur de la bordure&lt;/li&gt;
&lt;li&gt;BorderThickness : épaisseur de la bordure&lt;/li&gt;
&lt;li&gt;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)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"this"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Grid.Resources&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DoubleDividerConverter&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"DoubleDividerConverter"&lt;/span&gt; &lt;span class="na"&gt;Divider=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ImageSizeToCenterConverter&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"ImageSizeToCenterConverter"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ColorToBrushConverter&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"ColorToBrushConverter"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Grid.Resources&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt;
                       &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize}"&lt;/span&gt;
                       &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"{Binding PlaceholderImageSource}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"Image"&lt;/span&gt;
                     &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt;
                     &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize}"&lt;/span&gt;
                     &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"{Binding MainImageSource}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Ellipse&lt;/span&gt; &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt;
             &lt;span class="na"&gt;Stroke=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BorderColor, Converter={StaticResource ColorToBrushConverter}}"&lt;/span&gt;
             &lt;span class="na"&gt;StrokeThickness=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BorderThickness}"&lt;/span&gt;
             &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize}"&lt;/span&gt;
             &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageSize}"&lt;/span&gt;
             &lt;span class="na"&gt;IsVisible=&lt;/span&gt;&lt;span class="s"&gt;"{Binding IsBorderVisible}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ActivityIndicator&lt;/span&gt; &lt;span class="na"&gt;IsRunning=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Source={x:Reference Image}, Path=IsLoading}"&lt;/span&gt;
                       &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{x:Reference this}"&lt;/span&gt;
                       &lt;span class="na"&gt;IsVisible=&lt;/span&gt;&lt;span class="s"&gt;"{Binding IsLoaderEnabled}"&lt;/span&gt;
                       &lt;span class="na"&gt;Color=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BorderColor}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vous noterez juste l'apparition d'un nouveau &lt;code&gt;Converter&lt;/code&gt; : notre propriété &lt;code&gt;BorderColor&lt;/code&gt; transmet un type &lt;code&gt;Color&lt;/code&gt; alors que la propriété &lt;code&gt;Stroke&lt;/code&gt; de l'Ellipse attend une &lt;code&gt;Brush&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour le reste, ce n'est que du &lt;code&gt;Binding&lt;/code&gt; vers les &lt;code&gt;BindableProperty&lt;/code&gt; et des astuces que nous avons déjà vu dans la &lt;a href="https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-1-4mgo"&gt;première partie de l'article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nous avons désormais une image circulaire avancée parfaitement réutilisable dans notre projet ! Chouette !&lt;/p&gt;

&lt;p&gt;Nous en avons même deux : &lt;code&gt;CircleImage&lt;/code&gt; et &lt;code&gt;AdvancedCircleImage&lt;/code&gt; !&lt;/p&gt;

&lt;h2&gt;
  
  
  Utiliser le contrôle dans le code
&lt;/h2&gt;

&lt;p&gt;En réalité, vous savez déjà comment utiliser votre contrôle personnalisé puisque vous l'avez déjà fait en incorporant &lt;code&gt;CircleImage&lt;/code&gt; dans le XAML de &lt;code&gt;AdvancedCircleImage&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Il suffit de :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Déclarer le namespace auquel appartient votre contrôle dans votre &lt;code&gt;ContentPage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Utiliser le contrôle dans le XAML&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voici un exemple très simplifié :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage&lt;/span&gt; &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"CircleImageDemo.MainPage"&lt;/span&gt;
            &lt;span class="na"&gt;xmlns:ci=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:CircleImageDemo.CircleImage"&lt;/span&gt;
            &lt;span class="err"&gt;[...]&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;            
    &lt;span class="nt"&gt;&amp;lt;ci:AdvancedCircleImage&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt;
                            &lt;span class="na"&gt;MainImageSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding PhotoUrl}"&lt;/span&gt;
                            &lt;span class="na"&gt;PlaceholderImageSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding PlaceholderImage}"&lt;/span&gt;
                            &lt;span class="na"&gt;BorderColor=&lt;/span&gt;&lt;span class="s"&gt;"Yellow"&lt;/span&gt;
                            &lt;span class="na"&gt;BorderThickness=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;
                            &lt;span class="na"&gt;IsBorderVisible=&lt;/span&gt;&lt;span class="s"&gt;"{Binding IsBookmarked}"&lt;/span&gt;
                            &lt;span class="na"&gt;IsLoaderEnabled=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans le &lt;a href="https://github.com/SylvainMoingeon/CircleImageDemo" rel="noopener noreferrer"&gt;projet de démonstration&lt;/a&gt;, j'utilise l'&lt;code&gt;AdvancedCircleImage&lt;/code&gt; dans une liste (en fait un &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/bindable-layouts" rel="noopener noreferrer"&gt;BindableLayout&lt;/a&gt; mais ce n'est pas le sujet).&lt;/p&gt;

&lt;h2&gt;
  
  
  Le projet de démonstration
&lt;/h2&gt;

&lt;p&gt;Je vous redonne le lien vers le dépôt GitHub du projet : &lt;a href="https://github.com/SylvainMoingeon/CircleImageDemo" rel="noopener noreferrer"&gt;https://github.com/SylvainMoingeon/CircleImageDemo&lt;/a&gt; ainsi que la petite animation :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqpogy27yrxlu3hq5v1k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqpogy27yrxlu3hq5v1k.gif" width="350" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;L'&lt;code&gt;AdvancedCircleImage&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;h3&gt;
  
  
  Jetez-y un œil !
&lt;/h3&gt;

&lt;p&gt;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 &lt;a href="https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g"&gt;MVVM&lt;/a&gt;. Et si vous êtes arrivé jusqu'à la fin de cet article, c'est sans doute le cas !&lt;/p&gt;

&lt;h4&gt;
  
  
  StringFormat
&lt;/h4&gt;

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

&lt;h4&gt;
  
  
  Code / XAML spécifique à la plateforme
&lt;/h4&gt;

&lt;p&gt;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. &lt;/p&gt;

&lt;p&gt;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 :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtfdgikmk4fbdi3nfr1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtfdgikmk4fbdi3nfr1j.png" width="422" height="915"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il existe quelques fonctionnalités spécifiques aux plateformes directement accessibles dans le code commun, notamment pour &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/ios/" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; et &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/android/" rel="noopener noreferrer"&gt;Android&lt;/a&gt;. Ici, nous allons nous intéresser au &lt;code&gt;SafeArea&lt;/code&gt; d'iOS.&lt;/p&gt;

&lt;p&gt;Dans le XAML :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage&lt;/span&gt; 
&lt;span class="na"&gt;xmlns:ios=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"&lt;/span&gt;
&lt;span class="na"&gt;ios:Page.UseSafeArea=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;
&lt;span class="err"&gt;[...]&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    [...]
&lt;span class="nt"&gt;&amp;lt;/ContentPage&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60h2yb9kexaevbtac5nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60h2yb9kexaevbtac5nq.png" width="422" height="915"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  DesignTimeVisible
&lt;/h4&gt;

&lt;p&gt;Si vous utilisez le &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-previewer/?pivots=windows" rel="noopener noreferrer"&gt;Previewer Xamarin.Forms&lt;/a&gt;, 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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuazyzkxp3b8vgoxxgjmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuazyzkxp3b8vgoxxgjmr.png" alt="Copie d'écran présentant le previewer Xamarin.Forms, le contrôle personnalisé n'est pas affiché" width="800" height="661"&gt;&lt;/a&gt;L'image circulaire n'est pas affichée pas dans le previewer, quel malheur !&lt;/p&gt;

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

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;[XamlCompilation(XamlCompilationOptions.Compile)]
[DesignTimeVisible(true)]
public partial class AdvancedCircleImage : Grid
{
[...]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgucbd4qcemivnbm61npo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgucbd4qcemivnbm61npo.png" alt="Illustration du previewer avec l'attribut DesignTimeVisible" width="800" height="638"&gt;&lt;/a&gt;L'image circulaire est bien affichée grâce à l'attribut DesignTimeVisible(true)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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 !&lt;/p&gt;

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

</description>
      <category>xamarinforms</category>
      <category>xaml</category>
      <category>mvvm</category>
      <category>french</category>
    </item>
    <item>
      <title>Xamarin.Forms 5.0 en approche !</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Mon, 26 Oct 2020 16:05:57 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/xamarin-forms-5-0-en-approche-f42</link>
      <guid>https://dev.to/sylvainmoingeon/xamarin-forms-5-0-en-approche-f42</guid>
      <description>&lt;p&gt;Xamarin.Forms 5.0 pointe bientôt le bout de son nez et apporte son lot de nouveautés. Alors c'est vrai, pour la plupart il s'agit du passage en version stable de fonctionnalités qui étaient déjà disponibles en version expérimentale, mais c'est l'occasion d'en faire une récap et de prendre conscience des avancées qu'à fait Xamarin.Forms cette dernière année.&lt;/p&gt;




&lt;p&gt;Pas d'article fleuve pour cette fois, je me contente de vous présenter quelques nouveautés et de transmettre quelques liens vers les ressources qui vous donneront toutes les informations.&lt;/p&gt;

&lt;p&gt;La plupart des informations proviennent de la vidéo que vous trouverez en fin d'article et du post &lt;a href="https://devblogs.microsoft.com/xamarin/xamarin-forms-5-preview/" rel="noopener noreferrer"&gt;Xamarin.Forms 5 preview&lt;/a&gt; de David Ortinau.&lt;/p&gt;

&lt;h2&gt;
  
  
  Libère ta créativité avec les Brushes, Shapes et Paths
&lt;/h2&gt;

&lt;p&gt;L'un des derniers point faible de Xamarin.Forms vient de sauter !&lt;/p&gt;

&lt;p&gt;Créer des formes vectorielles, des dégradés ou des chemins complexes ne nécessitent plus aucune bibliothèque tierce. Les éléments visuels peuvent être découpés selon n'importe quelle géométrie ouvrant la porte à des design modernes et bien léchés. Tout cela grâce aux &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/brushes/" rel="noopener noreferrer"&gt;&lt;code&gt;Brush&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/" rel="noopener noreferrer"&gt;&lt;code&gt;Shape&lt;/code&gt;&lt;/a&gt; et &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/path" rel="noopener noreferrer"&gt;&lt;code&gt;Path&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9xq3pya4a8gsjr3uzah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9xq3pya4a8gsjr3uzah.png" alt="Illustration d'interface utilisateur utilisant les brushes, shapes et paths" width="800" height="831"&gt;&lt;/a&gt;Avec les brushes, shapes et paths, créer des design léchés devient un jeu d'enfant !&lt;/p&gt;

&lt;h2&gt;
  
  
  Templating, encore un pas de plus vers le "vrai" XAML de WPF
&lt;/h2&gt;

&lt;p&gt;Jusqu'à maintenant, on pouvait créer de nouveaux contrôles soit par composition de contrôles &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt; existant soit en créant un nouveau contrôle de toute pièce de façon native, c'est-à-dire en codant à partir de zéro une version par plateforme ciblée.&lt;/p&gt;

&lt;p&gt;La nouvelle version du &lt;code&gt;RadioButton&lt;/code&gt; va plus loin et s'approche de ce que WPF propose puisqu'on peut désormais en modifier complètement le contenu à l'aide du templating.&lt;/p&gt;

&lt;p&gt;Ça n'a l'air de rien dit comme ça, mais quand ce sera généralisé à tous les contrôles, ceux-ci pourront prendre n'importe quelle apparence sans toucher au code natif, sans recours à des custom renderers et donc sans besoin de créer de nouveaux contrôles. Cela représente donc une énorme partie du code spécifique aux plateformes qui disparaîtra au profit du code commun. Autant de temps de développement, de maintenance et de correction de bug en moins ! C'est un premier (grand) pas vers le &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt; 100% cross-plateforme et donc vers &lt;a href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/" rel="noopener noreferrer"&gt;MAUI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7n5b8u8o6jrctwkvtbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7n5b8u8o6jrctwkvtbr.png" alt="Illustration du templating sur un radioButton" width="800" height="264"&gt;&lt;/a&gt;Templating sur un RadioButton&lt;/p&gt;

&lt;h2&gt;
  
  
  Enfin un CarouselView pleinement fonctionnel et exploitable
&lt;/h2&gt;

&lt;p&gt;D'abord intégré en tant que type de Page puis transformé en Layout expérimental, le &lt;code&gt;CarouselView&lt;/code&gt; atteint enfin l'âge de la maturité et dispose désormais de tout le nécessaire pour en faire un vrai contrôle utilisable en production : support du &lt;code&gt;VisualStateManager&lt;/code&gt;, &lt;code&gt;IndicatorView&lt;/code&gt; (par exemple, les petits points qui indiquent sur quel item on est dans la liste), &lt;code&gt;PeekAreaInsets&lt;/code&gt; pour faire empiéter les items précédents et suivants sur l'item courant...&lt;/p&gt;

&lt;p&gt;Je vous renvois à la &lt;a href="https://docs.microsoft.com/fr-fr/xamarin/xamarin-forms/user-interface/carouselview" rel="noopener noreferrer"&gt;documentation sur le CarouselView&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ça glisse avec le SwipeView
&lt;/h2&gt;

&lt;p&gt;Aussi incroyable que cela puisse paraître, &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt; ne proposait encore aucune solution pour intégrer un mouvement de &lt;code&gt;Swipe&lt;/code&gt; sur les items d'une liste. On trouvait bien ici ou là quelques implémentations de ce comportement, mais rien d'officiel ou de bien stable. Au point que j'avais fini par créer mon propre &lt;a href="https://github.com/SylvainMoingeon/SwipableView" rel="noopener noreferrer"&gt;composant SwipableView pour Xamarin.Forms&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;C'est désormais de l'histoire ancienne, bienvenue au &lt;code&gt;SwipeView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkrk3igi37w2mlyxnns2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkrk3igi37w2mlyxnns2.gif" alt="Illustration animée du SwipeView Xamarin.Forms" width="83" height="168"&gt;&lt;/a&gt;SwipeView Xamarin.Forms&lt;/p&gt;

&lt;h2&gt;
  
  
  Autres nouveautés et infos en vrac
&lt;/h2&gt;

&lt;p&gt;Vous trouverez dans Xamarin.Forms 5.0 un nouveau &lt;a href="https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/gestures/drag-and-drop" rel="noopener noreferrer"&gt;gesture Drag and Drop&lt;/a&gt;, principalement dédié au surface duo avec ses deux écrans. Mais bien entendu, rien ne vous empêche de l'utiliser au sein de vos applications.&lt;/p&gt;

&lt;p&gt;Plusieurs contrôles ont migré dans le &lt;a href="https://github.com/xamarin/XamarinCommunityToolkit" rel="noopener noreferrer"&gt;Xamarin Community Toolkit&lt;/a&gt;, tels que &lt;code&gt;MediaElement&lt;/code&gt; et &lt;code&gt;Expander&lt;/code&gt;. Ceux-ci n'étaient pas encore prêts à passer en version finale dans Xamarin.Forms mais l'équipe souhaitait les faire sortir du statut expérimental. Ils sont donc disponibles dans le toolkit.&lt;/p&gt;

&lt;p&gt;Un changement de version majeure s'accompagne souvent de "breaking changes", ici  notamment :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MasterDetailPage et ses propriétés changent de nom suite aux polémiques récentes sur la connotation Maître/Esclave. On parlera désormais de &lt;code&gt;Flyout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Côté Apple, &lt;code&gt;UIWebView&lt;/code&gt; qui était déprécié a été complètement retirée&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/fr-fr/xamarin/xamarin-forms/release-notes/5.0/5.0.0-pre3" rel="noopener noreferrer"&gt;Le changelog complet de Xamarin.Forms 5.0&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  David Ortinau et James Montemagno vous présente tout ça en vidéo
&lt;/h2&gt;

&lt;p&gt;Votre récompense pour être allé au bout de l'article :&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ttF80UnrJAg"&gt;
&lt;/iframe&gt;
   &lt;/p&gt;

</description>
      <category>xamarinforms</category>
      <category>french</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Créer un contrôle réutilisable 100% Xamarin.Forms, partie 1</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Thu, 22 Oct 2020 10:13:26 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-1-4mgo</link>
      <guid>https://dev.to/sylvainmoingeon/creer-un-controle-reutilisable-100-xamarin-forms-partie-1-4mgo</guid>
      <description>&lt;p&gt;Dans cet article "&lt;a href="https://www.sylvainmoingeon.fr/tags/from-scratch/" rel="noopener noreferrer"&gt;from scratch&lt;/a&gt;" en deux parties, je vous présente une manière simple de créer une image circulaire avancée et réutilisable à partir de zéro, sans custom renderer, ni bibliothèque tierce.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sylvainmoingeon.fr/creer-un-controle-reutilisable-100-xamarinforms-partie-1/" rel="noopener noreferrer"&gt;Partie 1&lt;/a&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Créer une image circulaire à l'aide de la propriété &lt;code&gt;Clip&lt;/code&gt; et des &lt;code&gt;Geometry&lt;/code&gt; récemment apparues dans &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Afficher un indicateur d'activité pendant le chargement de l'image
&lt;/li&gt;
&lt;li&gt;Afficher une image de substitution en cas d'absence d'image ou d'url invalide en utilisant la technique de superposition dans une &lt;code&gt;Grid&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Afficher une bordure autour de l'image à l'aide des &lt;code&gt;Shape&lt;/code&gt;, autre nouveauté de &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Utiliser des &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/internals/experimental-flags" rel="noopener noreferrer"&gt;fonctionnalités expérimentales&lt;/a&gt; avec la version stable actuelle de &lt;a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/" rel="noopener noreferrer"&gt;Xamarin.Forms&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Partie 2 :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Créer un contrôle réutilisable et compatible &lt;a href="https://www.sylvainmoingeon.fr/tags/mvvm/" rel="noopener noreferrer"&gt;MVVM&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Définir des propriétés bindables (&lt;code&gt;BindableProperty&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Utiliser les &lt;code&gt;Converters&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Les présentations sont faites, place au concret.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Certaines fonctionnalités comme les &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/" rel="noopener noreferrer"&gt;Shapes&lt;/a&gt; et leurs &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/geometries" rel="noopener noreferrer"&gt;Geometries&lt;/a&gt; nécessitent Xamarin.Forms 4.8 ou supérieure, pensez bien à mettre-à-jour le package après avoir créé votre solution !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Commençons par la fin
&lt;/h2&gt;

&lt;p&gt;Pas de suspens, je vous livre d'emblée un visuel pour que vous sachiez où je vous amène. Celui-ci simule une page de contact :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SylvainMoingeon/CircleImageDemo" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcm5siwobyncylst2dsg.gif" alt="Démonstration de l'image circulaire avec ses différentes options" width="350" height="726"&gt;&lt;/a&gt;Démonstration de l'image circulaire avec ses différentes options&lt;/p&gt;

&lt;p&gt;Niveau contenu, la page affiche une liste de contacts dont chaque photo provient d'une source différente :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Une photo chargée à partir d'une url&lt;/li&gt;
&lt;li&gt;Une photo ajoutée au projet en tant que ressource&lt;/li&gt;
&lt;li&gt;Pas de photo&lt;/li&gt;
&lt;li&gt;Une photo pointant sur une url retournant une erreur 404&lt;/li&gt;
&lt;li&gt;Une photo pointant sur une url inaccessible&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Les trois derniers cas présentent une image de substitution par défaut car aucune photo valide ne peut être affichée.&lt;/p&gt;

&lt;p&gt;Les contacts enregistrés comme favoris sont signalés par une bordure jaune autour de la photo.&lt;/p&gt;

&lt;p&gt;Pour finir, si vous observez bien l'animation vous remarquerez un bref indicateur de chargement sur les deux dernières photos, le temps que Xamarin.Forms échoue à charger les images.&lt;/p&gt;

&lt;p&gt;Mais je vous vois frétiller d'impatience, vous êtes venu ici pour voir du code, alors allons-y&lt;/p&gt;

&lt;h2&gt;
  
  
  Clip clip clip, découpage de l'image
&lt;/h2&gt;

&lt;p&gt;Plutôt que de vous livrer de gros blocs de code illisibles sur une page web, je vais dans cet article aller directement à l'essentiel, vous trouverez le code complet sur mon &lt;a href="https://github.com/SylvainMoingeon" rel="noopener noreferrer"&gt;compte GitHub&lt;/a&gt;, comme d'habitude.&lt;/p&gt;

&lt;p&gt;Pour commencer, comment afficher une image circulaire ?&lt;/p&gt;

&lt;p&gt;Nous allons utiliser deux fonctionnalités apparues dernièrement dans Xamarin.Forms, la propriété &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/geometries#clip-with-a-geometry" rel="noopener noreferrer"&gt;Clip&lt;/a&gt; (&lt;em&gt;découper&lt;/em&gt; en anglais) et les &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/geometries" rel="noopener noreferrer"&gt;Geometries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Clip&lt;/code&gt; est une propriété des &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/xamarin.forms.visualelement" rel="noopener noreferrer"&gt;VisualElements&lt;/a&gt;, de type &lt;code&gt;Geometry&lt;/code&gt; et qui définit le contour du contenu d'un élément visuel. &lt;/p&gt;

&lt;p&gt;Ce n'est pas clair ? Un exemple, celui qui nous concerne : j'ai une image carrée et je souhaite afficher uniquement son contenu inscrit dans un cercle. Je vais donc définir une &lt;code&gt;Geometry&lt;/code&gt; circulaire et l'appliquer à &lt;code&gt;Clip&lt;/code&gt; pour découper l'image selon cette forme.&lt;/p&gt;

&lt;p&gt;Attention les yeux, voici le code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monimage.png"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"200"&lt;/span&gt; &lt;span class="na"&gt;Height=&lt;/span&gt;&lt;span class="s"&gt;"200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;
                         &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;
                         &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"100,100"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La géométrie circulaire n'existant pas, nous nous baserons sur une ellipse. Celle-ci se définit à partir d'un centre et de deux rayons, horizontal (&lt;code&gt;RadiusX&lt;/code&gt;) et vertical (&lt;code&gt;RadiusY&lt;/code&gt;). Pour obtenir un cercle, il suffit que &lt;code&gt;RadiusX&lt;/code&gt; et &lt;code&gt;RadiusY&lt;/code&gt; aient la même valeur. &lt;code&gt;Center&lt;/code&gt; permet de positionner l'ellipse par rapport à son contenant.&lt;/p&gt;

&lt;p&gt;Pour simplifier le calcul et le positionnement, autant partir d'une image carrée.&lt;/p&gt;

&lt;p&gt;Pour obtenir un cercle centré et inscrit dans le carré, les rayons seront moitié plus petits que le côté de l'image et le centre du cercle sera au centre de l'image.&lt;/p&gt;

&lt;p&gt;Voilà, obtenir une image circulaire c'est devenu aussi simple que ça avec Xamarin.Forms. Beaucoup de texte et d'explications pour quelque chose devenu finalement assez trivial !&lt;/p&gt;

&lt;h2&gt;
  
  
  L'image de substitution
&lt;/h2&gt;

&lt;p&gt;Comment faire pour afficher une image par défaut quand il n'y a pas d'image à afficher ?&lt;/p&gt;

&lt;p&gt;On va utiliser une technique ancienne et bien connue mais toujours très utile : l'empilement dans une &lt;code&gt;Grid&lt;/code&gt;. Les éléments contenus dans une même cellule d'une Grid se superposent les uns sur les autres, le dernier déclaré dans le fichier Xaml étant au-dessus, la lecture de celui-ci étant séquentielle.&lt;/p&gt;

&lt;p&gt;Nous allons donc définir une Grid à une seule cellule et y ajouter deux &lt;code&gt;Images&lt;/code&gt; : la première est destinée à afficher l'image de substitution, la seconde à afficher l'image proprement dite.&lt;/p&gt;

&lt;p&gt;Pour que les deux images se superposent sans décalage, il est nécessaire que les deux composants &lt;code&gt;Image&lt;/code&gt; et leurs ellipses aient les mêmes dimensions.&lt;/p&gt;

&lt;p&gt;Nous renseignerons également les propriétés &lt;code&gt;HorizontalOptions&lt;/code&gt; et &lt;code&gt;VerticalOptions&lt;/code&gt; de la Grid avec des valeurs qui forceront celle-ci à adopter les mêmes dimensions que son contenu.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://fr.wikipedia.org/wiki/Ne_vous_r%C3%A9p%C3%A9tez_pas" rel="noopener noreferrer"&gt;DRY&lt;/a&gt; - Je vais avoir besoin de créer deux fois une image circulaire simple, et il est probable que ce besoin se répète dans d'autres contextes. J'ai donc créé un contrôle &lt;code&gt;CircleImage&lt;/code&gt; simple encapsulant le code précédent. Nous verrons dans la seconde partie de l'article comment créer un contrôle réutilisable.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder.png"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"monImage.png"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De cette manière, l'image vient cacher le placeholder si elle est disponible, sinon on voit ce dernier à la place.&lt;/p&gt;

&lt;h2&gt;
  
  
  L'indicateur de chargement
&lt;/h2&gt;

&lt;p&gt;Aujourd'hui c'est atelier empilage, nous allons encore superposer : ajoutons un &lt;code&gt;ActivityIndicator&lt;/code&gt; à notre Grid.&lt;/p&gt;

&lt;p&gt;L'&lt;code&gt;ActivityIndicator&lt;/code&gt; a un visuel légèrement différent sous Android et iOS mais le principe reste le même : c'est un bidule qui tourne de façon hypnotique pour faire oublier le temps qui passe à l'utilisateur. Pour le voir tourner, il suffit d'affecter &lt;code&gt;True&lt;/code&gt; à sa propriété &lt;code&gt;IsRunning&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monImage"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ActivityIndicator&lt;/span&gt; &lt;span class="na"&gt;IsRunning=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;
                        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous avons donc un indicateur qui tourne au centre de notre image. C'est bien, mais il tourne en permanence et nous aurions besoin de l'activer uniquement pendant le chargement de l'image.&lt;/p&gt;

&lt;p&gt;Cela tombe bien, le contrôle &lt;code&gt;Image&lt;/code&gt; expose une propriété &lt;code&gt;IsLoading&lt;/code&gt; qui vaut &lt;code&gt;True&lt;/code&gt; pendant le chargement de l'image et &lt;code&gt;False&lt;/code&gt; une fois le chargement terminé, peu importe que l'opération ce soit bien déroulée ou non.&lt;/p&gt;

&lt;p&gt;Reste une question : comment lier la propriété &lt;code&gt;IsRunning&lt;/code&gt; de l'indicateur à la propriété &lt;code&gt;IsLoading&lt;/code&gt; de l'image ? Par un &lt;code&gt;Binding&lt;/code&gt; bien entendu ! Mais comment binder une propriété d'un contrôle à une propriété d'un autre contrôle ? En précisant la source du Binding !&lt;/p&gt;

&lt;p&gt;Le plus simple ici est de donner un nom explicite à notre contrôle Image (&lt;em&gt;monImage&lt;/em&gt; par exemple) et de se référer à ce nom comme source du Binding. La propriété visée sera définie comme &lt;code&gt;Path&lt;/code&gt; du Binding : &lt;code&gt;"{Binding Source={x:Reference monImage}, Path=IsLoading}"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Avec un peu plus de contexte, cela donne :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monImage"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ActivityIndicator&lt;/span&gt; &lt;span class="na"&gt;IsRunning=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Source={x:Reference monImage}, Path=IsLoading}"&lt;/span&gt;
                        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De cette façon, nous verrons un indicateur de chargement tournoyer au centre de l'image durant le chargement de celle-ci.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftd60sx9tpwmw9c9jjluc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftd60sx9tpwmw9c9jjluc.gif" alt="ActivityIndicator au centre d'une image" width="74" height="74"&gt;&lt;/a&gt;Et pourtant, elle tourne !&lt;/p&gt;

&lt;h2&gt;
  
  
  La bordure
&lt;/h2&gt;

&lt;p&gt;Pour afficher une bordure autour de l'image, nous allons tirer parti des &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/" rel="noopener noreferrer"&gt;&lt;code&gt;Shapes&lt;/code&gt;&lt;/a&gt; encore sous forme expérimentale sous Xamarin.Forms 4.8.&lt;/p&gt;

&lt;p&gt;Si la version de Xamarin.Forms que vous utilisez n'intègre pas encore les &lt;code&gt;Shapes&lt;/code&gt; de façon officielle, vous devrez déclarer la fonctionnalité expérimentale dans le constructeur de la classe &lt;code&gt;App.xaml.cs&lt;/code&gt; sous peine de lever une &lt;code&gt;InvalidOperationException&lt;/code&gt; au lancement de l'application :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Shapes_Experimental"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;MainPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MainPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les &lt;code&gt;Shapes&lt;/code&gt; comme leur nom l'indique sont des formes que l'on peut ajouter en tant que &lt;code&gt;VisualElement&lt;/code&gt; sur la page. La forme qui nous intéresse est là encore une &lt;code&gt;Ellipse&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Attention à ne pas confondre l'&lt;code&gt;EllipseGeometry&lt;/code&gt; qui est la &lt;strong&gt;description&lt;/strong&gt; d'un objet géométrique, avec l'&lt;code&gt;Ellipse&lt;/code&gt; qui est un contrôle Visuel !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Stroke&lt;/code&gt; et &lt;code&gt;StrokeThickness&lt;/code&gt; sont les deux propriétés exposées par l'Ellipse qui définissent la couleur et l'épaisseur de son contour. Nous n'aurons pas besoin des autres propriétés, mais nous pourrions affiner en définissant le type de contour (pointillé...) ou la couleur de remplissage de l'ellipse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Ellipse&lt;/span&gt; &lt;span class="na"&gt;Stroke=&lt;/span&gt;&lt;span class="s"&gt;"Yellow"&lt;/span&gt; &lt;span class="na"&gt;StrokeThickness=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, c'est juste une histoire d'empilage dans la &lt;code&gt;Grid&lt;/code&gt;, vous commencez à avoir l'habitude.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monPlaceholder"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;local:CircleImage&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"monImage"&lt;/span&gt; &lt;span class="na"&gt;ImageSize=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Ellipse&lt;/span&gt; &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
             &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
             &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
             &lt;span class="na"&gt;Stroke=&lt;/span&gt;&lt;span class="s"&gt;"Yellow"&lt;/span&gt;
             &lt;span class="na"&gt;StrokeThickness=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
             &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;
             &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"128"&lt;/span&gt;
             &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ActivityIndicator&lt;/span&gt; &lt;span class="err"&gt;[...]&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce qui donne :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F36%2Fcircle-with-border.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F36%2Fcircle-with-border.png" alt="Une image circulaire bordée de jaune" width="800" height="400"&gt;&lt;/a&gt;Une image circulaire bordée de jaune&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin de (première) partie
&lt;/h2&gt;

&lt;p&gt;L'article s'avère bien plus détaillé que je ne l'avais prévu au départ et ça commence à être bien consistant. Je vous laisse donc digérer tout ça et vous donne rendez-vous pour la seconde partie dans laquelle nous verrons comment assembler tout ceci pour en faire un contrôle réutilisable avec des propriétés Bindable pour le rendre "&lt;a href="https://www.sylvainmoingeon.fr/tags/mvvm/" rel="noopener noreferrer"&gt;MVVM&lt;/a&gt; friendly".&lt;/p&gt;

</description>
      <category>xamarinforms</category>
      <category>xaml</category>
      <category>fromscratch</category>
      <category>french</category>
    </item>
    <item>
      <title>L'inversion de dépendance facile (et pas chère)</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Tue, 29 Sep 2020 16:42:53 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/l-inversion-de-dependance-facile-et-pas-chere-51oe</link>
      <guid>https://dev.to/sylvainmoingeon/l-inversion-de-dependance-facile-et-pas-chere-51oe</guid>
      <description>&lt;p&gt;Ah, l'inversion de dépendance, voilà une pratique qui est parfois difficile à faire passer dans les équipes. En particulier quand il s'agit d'applications mobiles. Les arguments sont toujours les mêmes : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ça ajoute trop de complexité&lt;/li&gt;
&lt;li&gt;Ça bouffe des ressources et plombe les performances&lt;/li&gt;
&lt;li&gt;On n'en a pas besoin de toute façon on n'utilise qu'une seule implémentation&lt;/li&gt;
&lt;li&gt;Les frameworks c'est le mal absolu&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tu peux m'expliquer l'inversion de dépendance ? &lt;br&gt;
Fastoche, tu ouvres le gestionnaire de package nuget et tu installes AutoFac&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et si on trouvait un moyen simple, efficace et sans framework d'inverser les dépendances ?&lt;/p&gt;




&lt;h2&gt;
  
  
  Inversion et injection de dépendances
&lt;/h2&gt;

&lt;p&gt;Le but ici n'est pas de proposer un cours complet sur les principes d'inversion et d'injection de dépendances mais juste de présenter une astuce rapide, facile et pas chère pour effectuer une inversion de dépendance sans avoir recours à aucun framework.&lt;/p&gt;

&lt;p&gt;Si vous ne connaissez pas ces principes, je vous invite à vous rendre sur votre moteur de recherche préféré et d'y saisir les mots clés suivants (pas tous en même temps, hein !) : &lt;a href="https://lmgtfy.app/?q=inversion+de+d%C3%A9pendance" rel="noopener noreferrer"&gt;inversion de dépendance&lt;/a&gt; / &lt;a href="https://lmgtfy.app/?q=injection+d%C3%A9pendance" rel="noopener noreferrer"&gt;injection de dépendance&lt;/a&gt; / &lt;a href="https://lmgtfy.app/?q=dependency+inversion+principle" rel="noopener noreferrer"&gt;dependency inversion&lt;/a&gt; / &lt;a href="https://lmgtfy.app/?q=dependency+injection+principle" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt; / &lt;a href="https://lmgtfy.app/?q=constructor+injection" rel="noopener noreferrer"&gt;constructor injection&lt;/a&gt; / &lt;a href="https://lmgtfy.app/?q=ioc+di" rel="noopener noreferrer"&gt;ioc di&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vous verrez qu'assez souvent cela vous ramène à l'un ou l'autre des frameworks à la mode. Et dans les cas les plus complexes, c'est pertinent de ne pas réinventer la roue. Mais dans une application mobile, on a souvent besoin de quelque chose de beaucoup plus simple sans s'encombrer d'un framework ou d'une bibliothèque supplémentaire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi une inversion de contrôle ?
&lt;/h2&gt;

&lt;p&gt;Sans m'étendre sur le sujet, le but principal de l'inversion de contrôle est de ne pas laisser une classe dépendre d'autres classes. Elle ne doit dépendre que &lt;a href="https://fr.wikipedia.org/wiki/Abstraction_(informatique)#Programmation_orient%C3%A9e_objet" rel="noopener noreferrer"&gt;de leurs abstractions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;L'application de ce principe a des conséquences assez variées mais qui vont toutes dans le sens d'un code plus propre et mieux structuré :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Respect du principe de &lt;a href="https://fr.wikipedia.org/wiki/Principe_de_responsabilit%C3%A9_unique" rel="noopener noreferrer"&gt;responsabilité unique&lt;/a&gt;&lt;/strong&gt; : la classe ne voit que des abstractions, la responsabilité de ce qu'il s'y passe concrètement ne la regarde pas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Découplage&lt;/strong&gt; : les classes ne sont pas liées entre elles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modularité&lt;/strong&gt; : une classe n'étant pas liée à une implémentation particulière de ses dépendances, celle-ci est facilement interchangeable. Une même classe pourra, par exemple, lire les données dans une base Sql, un fichier json ou xml en fonction de l'implémentation du service d'accès aux données injecté.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testabilité&lt;/strong&gt; : puisqu'on ne dépend pas d'une implémentation concrète, on peut facilement utiliser des &lt;a href="https://fr.wikipedia.org/wiki/Mock_(programmation_orient%C3%A9e_objet)" rel="noopener noreferrer"&gt;simulacres&lt;/a&gt; pour effectuer des tests unitaires.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour l'astuce que je vais vous présenter très bientôt (je vous tiens en haleine !) nous allons passer les dépendances via le constructeur de la classe.&lt;/p&gt;

&lt;p&gt;L'intérêt est double :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Le code est lisible&lt;/strong&gt; car la liste des dépendances d'une classe est directement dans la signature de son constructeur : pas de surprise cachée dans le code de la classe&lt;/li&gt;
&lt;li&gt;On ne peut pas oublier de passer une dépendance à la classe puisqu'on en a besoin pour l'instancier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ici, les puristes auront sans doute les poils qui se dressent car ce que je vais vous montrer n'est pas au sens strict de l'injection de dépendance, mais pour les cas simples, ça en présente tous les avantages.&lt;/p&gt;

&lt;p&gt;Ce que nous allons chercher à faire c'est avoir pour une classe :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Une implémentation par défaut de ses dépendances&lt;/li&gt;
&lt;li&gt;La possibilité d'injecter d'autres implémentations en cas de besoin (notamment pour les tests unitaires)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  L'inversion de contrôle pas à pas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Partons de pas grand-chose
&lt;/h3&gt;

&lt;p&gt;Partons d'une classe ne suivant aucun pattern particulier, la dépendance est directement instanciée dans le corps de la classe. Beurk !&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaClasse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;MaDepdendance&lt;/span&gt; &lt;span class="n"&gt;_maDependance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_maDependance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MaDependance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Imaginez juste un instant que la classe &lt;code&gt;MaDependance&lt;/code&gt; dépende elle-même d'une autre classe, bienvenue au &lt;a href="https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4"&gt;code spaghetti et aux crottes de nez dans le code&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;Dans un premier temps, nous allons simplement chercher à passer la dépendance depuis l'extérieur. De cette façon, notre classe n'aura plus la responsabilité de l'instancier. Rien de bien compliqué.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaClasse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;MaDepdendance&lt;/span&gt; &lt;span class="n"&gt;_maDependance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// dépendance injectée via la constructeur&lt;/span&gt;
    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MaDependance&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_maDependance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Un message bien senti !"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Mieux mais pas terrible. Nous n'avons en réalité fait que déplacer le problème. Pour l'instant, toujours pas de découplage ou de modularité dans le code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dépendons de l'abstraction au lieu de l'implémentation
&lt;/h3&gt;

&lt;p&gt;Pour faire mieux, nous allons appliquer &lt;em&gt;stricto-sensu&lt;/em&gt; le principe d'inversion de contrôle : &lt;strong&gt;dépendre d'abstraction au lieu d'implémentation&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaClasse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMaDependance&lt;/span&gt; &lt;span class="n"&gt;_maDependance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// On injecte une abstraction (interface) au lieu d'une implémentation concrète&lt;/span&gt;
    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMaDependance&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_maDependance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Un message bien senti !"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Vous avez vu ? Le "I" pour &lt;code&gt;interface&lt;/code&gt; ? &lt;/p&gt;

&lt;p&gt;Ce n'est bien entendu qu'une convention de nommage, mais en réalité il s'est passé ceci :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// interface décrivant les membres à implémenter&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IMaDependance&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;MaMethode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// MaDependance implémente désormais l'interface IMaDependance&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaDependance&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMaDependance&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MaMethode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// implémentation de l'interface&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Au lieu de passer directement l'implémentation concrète de la dépendance, nous avons passé son abstraction. Chouette !&lt;/p&gt;

&lt;p&gt;Reste un souci. Sans l'aide d'un framework qui ferait ça dynamiquement, il reste nécessaire de passer explicitement les dépendances au constructeur de notre classe lors de son instanciation.&lt;/p&gt;

&lt;p&gt;Double problème :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cela peut vite devenir très lourd si la classe a plusieurs dépendances&lt;/li&gt;
&lt;li&gt;Que se passe t'il si l'on souhaite changer d'implémentation alors que celle-ci est passée en dur partout dans le code ?&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Ajoutons une implémentation par défaut
&lt;/h3&gt;

&lt;p&gt;Pour ajouter une implémentation pour défaut, nous allons simplement tirer parti du mot clé &lt;code&gt;this&lt;/code&gt; appliqué au constructeur de la classe. &lt;code&gt;this&lt;/code&gt; permet, en effet, d'appeler un constructeur à partir d'un autre constructeur.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaClasse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMaDependance&lt;/span&gt; &lt;span class="n"&gt;_maDependance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Constructeur acceptant un argument&lt;/span&gt;
    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMaDependance&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_maDependance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Un message bien senti !"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Constructeur vide, qui par appelle l'autre constucteur avec 'this'&lt;/span&gt;
    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MaDepdendance&lt;/span&gt;&lt;span class="p"&gt;()){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;De cette manière, le constructeur vide appellera systématiquement le constructeur ayant la dépendance en argument en lui passant une implémentation concrète.&lt;/p&gt;

&lt;p&gt;Nous obtenons donc deux manières d'instancier notre classe : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avec le constructeur vide : la dépendance sera implémentée par défaut&lt;/li&gt;
&lt;li&gt;Avec l'autre constructeur : la dépendance sera implémentée &lt;em&gt;manuellement&lt;/em&gt; par le développeur&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nous avons donc atteint notre objectif premier avec un simple mot clé &lt;code&gt;this&lt;/code&gt; du langage C# &lt;/p&gt;

&lt;p&gt;C'est bien, mais il subsiste un problème : l'implémentation par défaut est instanciée en dur dans le code :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si on suit les principes &lt;a href="https://fr.wikipedia.org/wiki/SOLID_%28informatique%29" rel="noopener noreferrer"&gt;SOLID&lt;/a&gt;, &lt;strong&gt;une classe doit être évolutive sans modification&lt;/strong&gt;. Ici, ce n'est pas le cas : le jour où on aura besoin de changer d'implémentation par défaut, il sera nécessaire de modifier le code au niveau du constructeur.&lt;/li&gt;
&lt;li&gt;Si plusieurs classes dépendent du même service, on risque simplement d'oublier de modifier le constructeur de l'une ou l'autre des classes qui utilisent &lt;code&gt;MaDependance&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trouvons donc un moyen de globaliser la correspondance entre une abstraction et son implémentation par défaut. C'est-à-dire, qu'en pratique, &lt;strong&gt;pour une interface donnée on obtienne systématiquement la même implémentation&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prenons une bonne résolution
&lt;/h3&gt;

&lt;p&gt;Avec Xamarin.Forms nul besoin de chercher très loin, le &lt;code&gt;DependencyService&lt;/code&gt; jouera ce rôle à merveille. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A strictement parler, le &lt;code&gt;DependencyService&lt;/code&gt; sert surtout à la résolution d'implémentations natives (dans les projets iOS, Android...) pour les utiliser dans le projet commun Xamarin.Forms.&lt;br&gt;&lt;br&gt;
En pratique, cela fonctionne très bien pour résoudre n'importe quel type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le &lt;code&gt;DependencyService&lt;/code&gt; expose principalement trois méthodes : &lt;code&gt;Register&lt;/code&gt;, &lt;code&gt;Get&lt;/code&gt; et &lt;code&gt;Resolve&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Register&lt;/code&gt; ne souffre pas d’ambiguïté, c'est ici que nous enregistrerons notre implémentation par défaut.&lt;/p&gt;

&lt;p&gt;Pour la simplicité de la démonstration, et parce qu'en pratique ce sera souvent le cas, je prends la classe &lt;code&gt;App.xaml.cs&lt;/code&gt; comme point d'entrée pour enregistrer mes dépendances.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// On enregistre le type MaDependance pour l'interface IMaDependance&lt;/span&gt;
        &lt;span class="n"&gt;DependencyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMaDependance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MaDependance&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="n"&gt;MainPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MainPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//[...]&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Faites comme vous voulez, ce qui est important c'est de bien enregistrer les types avant toute utilisation du &lt;code&gt;DependencyService&lt;/code&gt; dans le code, cela va de soi.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Get&lt;/code&gt; et &lt;code&gt;Resolve&lt;/code&gt; semblent fonctionner de manière identique, cependant la documentation affichée dans Visual Studio nuance quelque peu leur usage :&lt;/p&gt;

&lt;p&gt;_ &lt;strong&gt;Resolve&lt;/strong&gt; : The method to use to resolve dependencies by type&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Get&lt;/strong&gt; : Returns the platform-specific implementation of type T_&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Resolve&lt;/code&gt; semble mieux correspondre à notre besoin.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaClasse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMaDependance&lt;/span&gt; &lt;span class="n"&gt;_maDependance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMaDependance&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_maDependance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maDependance&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Un message bien senti !"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// On résoud le type avec le DependencyService&lt;/span&gt;
    &lt;span class="nf"&gt;MaClasse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DependencyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMaDependance&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Désormais, notre classe ne dépend plus d'aucune implémentation concrète, elle n'est plus qu'abstraction ! &lt;/p&gt;

&lt;p&gt;Imaginons que de nombreuses classes dépendent de &lt;code&gt;IMaDependance&lt;/code&gt; et qu'on ait besoin de changer l'implémentation de la dépendance partout dans le code, il suffira de remplacer l'implémentation enregistrée dans &lt;code&gt;DependencyService.Register&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour le reste, ça n'a pas changé : pour injecter une implémentation différente il est nécessaire d'instancier la classe en passant explicitement la dépendance dans le constructeur. Ce sera notamment le cas pour les simulacres créés à fin de tests unitaires.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Si l'inversion de contrôle est souvent confondue avec le framework qui la met en oeuvre, il s'agit en réalité d'un principe à l'énoncé plutôt simple : &lt;em&gt;une classe doit dépendre d'abstractions et non d'implémentations&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Et dans les cas simples, on peut l'appliquer &lt;strong&gt;sans framework&lt;/strong&gt; et profiter de ses avantages à moindre frais : &lt;strong&gt;découplage&lt;/strong&gt; , &lt;strong&gt;modularité&lt;/strong&gt; , &lt;strong&gt;testabilité&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Je vous en ai présenté ici une façon fort simple à base d'&lt;code&gt;interface&lt;/code&gt;, d'appel à un constructeur par défaut avec le mot clé &lt;code&gt;this&lt;/code&gt; et du &lt;code&gt;DependencyService&lt;/code&gt; de Xamarin.Forms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pour un projet mobile, c'est souvent largement suffisant !&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Le code source
&lt;/h2&gt;

&lt;p&gt;Comme toujours, un petit projet d'exemple sur mon &lt;a href="https://github.com/SylvainMoingeon/DITheEasyWayDemo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Celui-ci est minimaliste : un service, un ViewModel et un test unitaire. Son seul intérêt est de démontrer le fonctionnement de tout cela de façon un peu moins théorique.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/SylvainMoingeon" rel="noopener noreferrer"&gt;
        SylvainMoingeon
      &lt;/a&gt; / &lt;a href="https://github.com/SylvainMoingeon/DITheEasyWayDemo" rel="noopener noreferrer"&gt;
        DITheEasyWayDemo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Projet d'accompagnement pour mon article concernant l'injection de dépendance
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;DITheEasyWayDemo&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;Projet d'accompagnement pour mon article concernant &lt;a href="https://www.sylvainmoingeon.fr/xamarinforms-inversion-de-dependance-facile-et-pas-chere/" rel="nofollow noopener noreferrer"&gt;l'inversion de dépendance sous Xamarin.Forms&lt;/a&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/SylvainMoingeon/DITheEasyWayDemo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Et chez vous l'inversion de contrôle, ça se passe comment ?&lt;/p&gt;

</description>
      <category>xamarinforms</category>
      <category>ioc</category>
      <category>bonnespratiques</category>
      <category>solid</category>
    </item>
    <item>
      <title>[Xamarin.Forms] MVVM - 1 - A quoi ça sert ?</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Fri, 04 Sep 2020 17:27:37 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g</link>
      <guid>https://dev.to/sylvainmoingeon/xamarin-forms-mvvm-1-a-quoi-ca-sert-p5g</guid>
      <description>&lt;p&gt;Je m'en souviens encore, mon premier projet WPF où avec l'habitude des WinForms j'ai commencé à coder la logique métier dans le code-behind de la fenêtre. J'ai alors senti un regard lourd dans mon dos, un ricanement et, tel le Denis Brogniart du code, mon collègue qui me lâche son irrévocable sentence :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Avec WPF, tu dois faire du MVVM. D'ailleurs, à partir d'aujourd'hui, on va tous faire du MVVM.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et donc, on s'y est tous mis. De façon purement dogmatique, sans rien y comprendre. Ni le pourquoi, ni le comment. Sans connaître ses véritables raisons d'être, sans en avoir étudié les avantages et contraintes. Sans même avoir conscience qu'il s'agissait d'une architecture logicielle et donc de la fondation de notre applicatif.&lt;/p&gt;

&lt;p&gt;Alors donc, ce fût un grand moment de n'importe quoi ou chacun cherchait par tous les moyens à contourner les &lt;em&gt;problèmes&lt;/em&gt; posés par MVVM. &lt;strong&gt;Et quand tu commences à considérer ton architecture comme un problème, c'est qu'il y en a un sérieux dans l'équipe de développement&lt;/strong&gt;. Bien entendu, les ressources sur le sujet étaient ténues à l'époque et consistaient principalement à la résolution des &lt;em&gt;problèmes&lt;/em&gt; apportés par MVVM. Oui, on était tous dans le même bateau.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkusx9cw96ib6jjxnoea3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkusx9cw96ib6jjxnoea3.jpg" alt="Les candidats de Koh Lanta sautent du bateau" width="800" height="451"&gt;&lt;/a&gt;Revenez, ce n'est pas si terrible MVVM !&lt;/p&gt;

&lt;p&gt;Heureusement, une dizaine d'années plus tard tout cela a bien changé et... hein ? Quoi ? C'est toujours comme ça ? Des équipes qui &lt;em&gt;font du MVVM&lt;/em&gt; sans réfléchir, sans le comprendre, juste parce qu'on leur a dit &lt;em&gt;c'est comme ça&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Il est peut-être temps de prendre un peu de recul et de comprendre à quoi ça sert.&lt;/p&gt;




&lt;h2&gt;
  
  
  MVVM, mais ça vient d'où ?
&lt;/h2&gt;

&lt;p&gt;Sans refaire un historique complet des technologies Microsoft, sachez qu'avec .NET 3.0 (sorti en 2007 il me semble) est apparu un nouveau paradigme de programmation, en particulier au niveau graphique. Exit les vieilles fenêtres grises figées sur lesquelles on n'avait aucune liberté visuelle et dans lesquelles on mélangeait allègrement l'interface utilisateur et le code métier. Bienvenue à la conception graphique via la technologie &lt;a href="https://docs.microsoft.com/en-us/visualstudio/xaml-tools/xaml-overview?view=vs-2019" rel="noopener noreferrer"&gt;XAML&lt;/a&gt;. Le développeur s'est alors retrouvé devant deux langages distincts pour traiter l'affichage ou le code :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XAML : un langage déclaratif au gout de XML pour décrire l'interface graphique&lt;/li&gt;
&lt;li&gt;Le code (C#, VB.Net...) : pour la logique, le code métier...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voici un exemple de page Xamarin.Forms décrite en XAML :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://xamarin.com/schemas/2014/forms"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:local=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:XamlSamples"&lt;/span&gt;
             &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"XamlSamples.MainPage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;StackLayout&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Place new controls here --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Welcome to Xamarin Forms!"&lt;/span&gt;
               &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
               &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/StackLayout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bien entendu, il a fallu d'une façon ou d'une autre faire communiquer code et affichage. C'est pourquoi le concept de &lt;a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/" rel="noopener noreferrer"&gt;DataBinding&lt;/a&gt; est devenu central avec XAML : les différentes propriétés des contrôles visuels (texte, couleur, visibilité, switch...) sont liées à des propriétés dans le code et reflètent leurs valeurs et changement de valeurs de façon autonome et automatique.&lt;/p&gt;

&lt;p&gt;Par exemple, une liste sera affichée en étant "bindée" à une collection dans le code. Une case à cocher sera "bindée" à une propriété booléenne...&lt;/p&gt;

&lt;p&gt;Ici, même exemple que précédemment mais le texte du &lt;code&gt;Label&lt;/code&gt; est "bindé" à la propriété &lt;code&gt;RandomQuote&lt;/code&gt; d'une classe. On peut imaginer, par exemple, que &lt;code&gt;RandomQuote&lt;/code&gt; est une citation célèbre extraite aléatoirement d'une liste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://xamarin.com/schemas/2014/forms"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:local=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:XamlSamples"&lt;/span&gt;
             &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"XamlSamples.MainPage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;StackLayout&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Place new controls here --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding RandomQuote}"&lt;/span&gt;
               &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
               &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/StackLayout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mais qui est chargé d'exposer la propriété &lt;code&gt;RandomQuote&lt;/code&gt; ? Comment les changements de valeurs de cette propriété seront propagés jusqu'au XAML ? Et inversement si la valeur est modifiée par l'utilisateur au niveau de l'interface, comment l'information est remontée dans la classe d'origine ?&lt;/p&gt;

&lt;p&gt;C'est pour répondre à ces questions et architecturer tout ceci que Microsoft a proposé un nouveau modèle (repris depuis un peu partout) : &lt;a href="https://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-vue_mod%C3%A8le" rel="noopener noreferrer"&gt;MVVM, c'est-à-dire Model-View-ViewModel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Ce qu’il faut en retenir c’est que cette architecture a pour but de découpler les données (Model), la logique (ViewModel) et l’interface utilisateur (View). *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;La View est décrite en XAML de façon purement déclarative. Elle dispose tout de même d'un code-behind dans lequel on peut coder en C#, mais cela doit se limiter à du code lié à l'affichage (déclencher une animation par exemple).&lt;/p&gt;

&lt;p&gt;Le Model est la représentation d'une donnée, au sens métier du terme. &lt;/p&gt;

&lt;p&gt;Le ViewModel est la couche intermédiaire qui prépare les données et les expose à la View. C'est généralement là qu'on trouvera la logique de validation des données, l'exécution des calculs, l'appel aux différents services...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2g9coh7qxj795fjl8y8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2g9coh7qxj795fjl8y8.png" width="771" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il y a donc un découpage assez net et précis entre ce qui concerne l'affichage, ce qui va stocker les données et ce qui va préparer les données.&lt;/p&gt;

&lt;p&gt;C'est là tout l'enjeu de MVVM, et pour que ça ne parte pas en cacahuètes, il y a bien entendu un certain nombre de règles à respecter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Du bon usage de MVVM
&lt;/h2&gt;

&lt;p&gt;MVVM, c'est comme un &lt;a href="https://fr.wikipedia.org/wiki/Gremlins" rel="noopener noreferrer"&gt;mogwai&lt;/a&gt;, ça a l'air tout mignon &lt;strong&gt;tant qu'on suit scrupuleusement les règles&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1s5h514veyoxqezown4k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1s5h514veyoxqezown4k.jpg" alt="Guizmo" width="800" height="448"&gt;&lt;/a&gt;MVVM... tant qu'on respecte les règles !&lt;/p&gt;

&lt;h3&gt;
  
  
  Principes de base
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;La View ne se préoccupe que de l'affichage et de l'interaction utilisateur.&lt;/strong&gt;  Elle ne contient ni ne manipule aucune donnée !&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le ViewModel est agnostique de la View&lt;/strong&gt;. Il ne sait ni quelle vue l'utilise ni comment elle compte le manipuler. Il sert à préparer les données à partir de la couche Model mais ne contient pas, en principe, de logique métier en lui-même.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le Model est agnostique du ViewModel et de la View&lt;/strong&gt;. Un Model est parfaitement indépendant du reste du code, il est en mesure de vivre sa vie tout seul. Un Model est réutilisable directement dans un autre projet qui utiliserait la même couche métier, par exemple.&lt;/li&gt;
&lt;li&gt;Ne jamais, mais alors ne jamais le nourrir après minuit !&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, je vous parle depuis tout à l'heure de View, de Model et de ViewModel, mais tout cela reste bien abstrait, voyons donc ça plus en détails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le M, le V et le VM
&lt;/h3&gt;

&lt;h4&gt;
  
  
  V comme View
&lt;/h4&gt;

&lt;p&gt;La View est un élément visuel, une page par exemple, qui définit la mise-en-page et l'apparence, mais également l'interaction utilisateur (boite de dialogue...).&lt;/p&gt;

&lt;p&gt;La View référence un ViewModel via sa propriété &lt;code&gt;BindingContext&lt;/code&gt;. Les contrôles sur la View héritent du BindingContext de la View et sont liés à des propriétés et des commandes exposée par le ViewModel via le système de &lt;code&gt;DataBinding&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Un DataBinding peut être personnalisé de diverses manières (validation, &lt;a href="https://www.sylvainmoingeon.fr/tags/converters/" rel="noopener noreferrer"&gt;converters&lt;/a&gt;, changement de BindingContext...) mais nous y reviendrons en détail dans d'autres articles.&lt;/p&gt;

&lt;p&gt;Toute la logique d'affichage qui ne peut pas être directement traité en XAML est codée dans le code-behind de la page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;La View définit le comportement visuel et réagit aux changements d'états du ViewModel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  VM comme ViewModel
&lt;/h3&gt;

&lt;p&gt;Le ViewModel est une classe non visuelle et qui ne référence en aucune façon ni la View ni aucun élément relatif à l'affichage. &lt;strong&gt;Il encapsule la &lt;em&gt;logique de présentation&lt;/em&gt; mais pas son affichage&lt;/strong&gt;. J'insiste car c'est une règle trop souvent bafouée et qui mène irrémédiablement au monstre spaghetti.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdgnaal3cmwnf4fuc6gl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdgnaal3cmwnf4fuc6gl.jpg" alt="Un beau plat de spaghetti" width="620" height="465"&gt;&lt;/a&gt;MVVM... quand on commence à référencer la View dans le ViewModel&lt;/p&gt;

&lt;p&gt;Le ViewModel expose des propriétés et des commandes auxquelles la View se lie par DataBinding : il notifie la View de ses changements d'états à travers l'interface &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;En pratique, on crée souvent une classe de base qui implémente &lt;code&gt;INotifyPropertyChanged&lt;/code&gt; et dont tous les ViewModels vont hériter. C'est plus simple comme ça.&lt;/p&gt;

&lt;p&gt;Les listes d'objets sont généralement exposées sous forme d'&lt;code&gt;ObservableCollections&lt;/code&gt; qui implémentent &lt;code&gt;INotifyCollectionChanged&lt;/code&gt; pour informer la View de la suppression ou de l'ajout d'éléments dans la liste.&lt;/p&gt;

&lt;p&gt;Le cas échéant, c'est le ViewModel qui contient la logique propre à l'application (mais pas la logique métier, en principe).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Le ViewModel se charge de préparer et de mettre en forme les données pour la View.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  M comme Model
&lt;/h3&gt;

&lt;p&gt;Le Model encapsule les données, la logique métier et la validation, même si c'est sujet à discussion. En pratique, la validation est parfois traitée directement par le ViewModel.&lt;/p&gt;

&lt;p&gt;Les Models ne référencent jamais ni la View, ni le ViewModel. Ce sont des entités indépendantes.&lt;/p&gt;

&lt;p&gt;Ils implémentent la plupart du temps &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;En fait, il y a deux écoles : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le ViewModel expose directement les Models et donc ceux-ci implémentent impérativement &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Le ViewModel encapsule les propriétés des Models et ce sont les propriétés du ViewModel qui sont bindée à la View. Dans ce cas, c'est le ViewModel qui implémente &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Qui a raison, qui a tort ? J'ai envie de dire, ça dépend du projet et du modèle de données. A titre personnel, je préfère encapsuler les propriétés dans le ViewModel, c'est bien plus propre : la View ne voit que ce dont elle a besoin.&lt;/p&gt;

&lt;h3&gt;
  
  
  R comme Résumé !
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Pour résumer, le ViewModel prépare et expose des données pour que la View les affiche à l’écran. Mais le ViewModel ne sait pas ce qu’en fait la View, c’est à la View de se débrouiller avec ce que le ViewModel lui fournit.  &lt;/p&gt;

&lt;p&gt;Les Models sont indépendants à la fois de la View et du ViewModel. Ce sont eux qui contiennent la logique métier, bien que ce sujet soit ouvert à discussion (la doc Microsoft n'est pas toujours en accord avec elle-même à ce propos !).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Avantages de MVVM
&lt;/h3&gt;

&lt;p&gt;Le but de MVVM est de découpler au maximum l'affichage de la logique et la logique des données. Mais pourquoi me direz-vous ?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pour rendre la logique (ViewModels) et les données (Models) unitairement testables sans avoir d'interaction avec l'interface.&lt;/li&gt;
&lt;li&gt;Pour rendre l'interface graphique testable indépendamment des données ! On peut, en effet, &lt;em&gt;mocker&lt;/em&gt; les ViewModels et injecter des données de test directement dans la vue !&lt;/li&gt;
&lt;li&gt;Pour éviter que les données encapsulent la logique métier / Pour éviter que la présentation des données encapsule la logique métier (rayez la mention inutile)&lt;/li&gt;
&lt;li&gt;Pour élargir le &lt;a href="https://fr.wikipedia.org/wiki/Principe_de_responsabilit%C3%A9_unique" rel="noopener noreferrer"&gt;Principe de Responsabilité Unique&lt;/a&gt; à l'affichage et à la présentation des données.&lt;/li&gt;
&lt;li&gt;Pour éviter qu'une modification au niveau de l'affichage ait des répercussions sur le code (ceux qui ont codé en VB6 savent de quoi je parle...)&lt;/li&gt;
&lt;li&gt;Pour rendre le code modulable et réutilisable, en particulier la couche métier.&lt;/li&gt;
&lt;li&gt;Dans le monde des bisounours, on dit aussi que ça permet aux designers et aux développeurs de travailler séparément sans avoir à attendre que l'un ou l'autre ait terminé sa tâche. Dans le vrai monde, euh, vous avez un designer qui ne s'occupe que de dessiner les interfaces dans votre équipe ?&lt;/li&gt;
&lt;li&gt;Pour rendre l'architecture du code lisible, on sait quoi va où&lt;/li&gt;
&lt;li&gt;Pour rendre le code maintenable : il est découpé en briques élémentaires indépendantes. En modifier une n'aura pas de répercussions inattendues à l'autre bout du code&lt;/li&gt;
&lt;li&gt;Pour éviter de &lt;a href="https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4"&gt;coller des crottes de nez&lt;/a&gt; dans le code.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Pour résumer, MVVM c'est une place pour chaque chose et chaque chose à sa place.&lt;br&gt;&lt;br&gt;
L'idéal est que chaque Model n'ait aucune dépendance vis-à-vis des ViewModels et des Views et que chaque ViewModels n'ait aucune dépendance vis-à-vis des Views.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Les contraintes de MVVM
&lt;/h3&gt;

&lt;p&gt;Alors, MVVM, c'est tout beau tout rose ?&lt;/p&gt;

&lt;p&gt;Evidemment, raconté comme ça, ça a l'air d'être la panacée. En réalité, &lt;strong&gt;MVVM est une architecture logicielle&lt;/strong&gt; et vient donc avec un certain nombre de règles et de contraintes.&lt;/p&gt;

&lt;p&gt;C'est comme le code de la route. Il est là pour garantir la sécurité de tous et éviter les accidents, mais cela ne vient pas sans quelques restrictions à nos libertés (s'arrêter au rouge, ne pas rouler à contresens...).&lt;/p&gt;

&lt;p&gt;La principale question qui se pose quand on se lance pour la première fois dans un projet architecturé autour de MVVM, c'est comment gérer les interactions utilisateurs dans le ViewModel.&lt;/p&gt;

&lt;p&gt;Je vous donne un exemple, sans doute le premier auquel vous serez confronté : je clique sur un bouton &lt;code&gt;SUPPRIMER&lt;/code&gt;, une boite de dialogue me demande de confirmer avant suppression. Simple n'est-ce pas ? Sauf que le code de suppression est dans le ViewModel, que la commande liée au bouton s'y exécute directement et donc, comment fait-on pour afficher une boite de dialogue quand on est dans un ViewModel qui ne doit rien savoir de l'affichage ?&lt;/p&gt;

&lt;p&gt;Eh bien, ce n'est pas simple !&lt;/p&gt;

&lt;p&gt;Et là, deux écoles s'affrontent : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ceux qui savent pourquoi ils utilisent MVVM et cherchent à continuer à en respecter les principes et bonnes pratiques. Cela demande plus de travail, cela demande de réellement réfléchir à l'architecture de l'application, mais ça assure une application bien structurée et maintenable dans le temps&lt;/li&gt;
&lt;li&gt;Ceux qui ne se souviennent plus (ou n'ont jamais su) pourquoi ils utilisent MVVM et cherchent a résoudre le &lt;em&gt;problème&lt;/em&gt; par toutes les plus mauvaises solutions qui soient. Celles qu'ils considèrent comme plus simples et rapides et qui au final transforment le code en un Gremlins qui au fil du temps est de plus en plus incontrôlable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1bpqorqm9gvvrps19dq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1bpqorqm9gvvrps19dq.jpg" alt="Le chef des Gremlins revisite massacre à la tronçonneuse" width="800" height="534"&gt;&lt;/a&gt;MVVM... quand tu ne respectes pas les règles !&lt;/p&gt;

&lt;p&gt;Un exemple très simple, rencontré personnellement sur un projet :&lt;/p&gt;

&lt;p&gt;Une page de l'application freezait de façon &lt;em&gt;inexplicable&lt;/em&gt;. Je n'ai pas mis longtemps à lever le lièvre : la View était injectée dans le ViewModel !&lt;/p&gt;

&lt;p&gt;Cette violation directe de MVVM a conduit à un effet extrêmement pervers : un &lt;a href="https://fr.wiktionary.org/wiki/deadlock" rel="noopener noreferrer"&gt;deadlock&lt;/a&gt;. En fait, le ViewModel exécutait une méthode déclarée dans le code-behind de la View qui elle-même exécutait une méthode du ViewModel qui attendait un résultat de la View ! Et patatra ! Les pieds dans le tapis !&lt;/p&gt;

&lt;p&gt;Cela a été corrigé en redécoupant proprement le code. Certes, cela a demandé plus de réflexion et mis plus de temps que le code départ qui était &lt;em&gt;plus simple et plus rapide&lt;/em&gt; à mettre en place. Mais maintenant ça fonctionne et &lt;strong&gt;surtout ça continue de fonctionner&lt;/strong&gt; malgré les évolutions de l'application ! Au final, &lt;strong&gt;prendre son temps fait gagner du temps&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En fait, le véritable défi avec MVVM est d'assurer la communication entre les différentes couches tout en prenant soin qu'elles ne se référencent pas les unes et les autres.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Il y a encore beaucoup à dire sur MVVM mais cet article est déjà bien trop long et je souhaitais rester à un certain niveau d'abstraction, n'aborder que les concepts de base.&lt;/p&gt;

&lt;p&gt;J'espère que vous comprenez mieux les enjeux de MVVM, ses raisons d'être.&lt;/p&gt;

&lt;p&gt;Si vous avez des questions ou si vous n'êtes pas d'accord (vous avez le droit !) laissez-moi un commentaire !&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Je voudrais juste terminer en précisant que MVVM est un choix d'architecture, pas quelque chose qu'on vous impose et qu'il faut contourner !&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Si MVVM ne vous convient pas, il existe des alternatives. Plutôt que massacrer MVVM, trouvez l'architecture qui convient à votre projet !&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>xamarinforms</category>
      <category>xaml</category>
      <category>mvvm</category>
      <category>french</category>
    </item>
    <item>
      <title>Développez des applications sans crotte de nez !</title>
      <dc:creator>Sylvain</dc:creator>
      <pubDate>Wed, 26 Aug 2020 14:37:33 +0000</pubDate>
      <link>https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4</link>
      <guid>https://dev.to/sylvainmoingeon/developpez-des-applications-sans-crotte-de-nez-mc4</guid>
      <description>&lt;p&gt;Récemment, j’ai entendu un développeur tenir les propos suivants :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Non mais ça ne sert à rien de faire du bon code, tout ce que le client demande c’est que ça fonctionne.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et malheureusement, sur la deuxième partie de l’assertion il n’a pas tort. Mais c’est parce que le client ne connait pas le métier et n’a aucune idée des répercussions sur le moyen/long terme d’un code de mauvaise qualité. Alors il faut que ça cesse !&lt;/p&gt;

&lt;p&gt;Et si au lieu de profiter de la situation pour justifier du travail de sagouin, on en profitait plutôt pour prendre ses responsabilités ?&lt;/p&gt;




&lt;h1&gt;
  
  
  Être vraiment professionnel
&lt;/h1&gt;

&lt;p&gt;Laissez-moi vous raconter une histoire :&lt;/p&gt;

&lt;p&gt;Vous vous rendez à la boulangerie, ça sent bon le pain chaud, la baguette est bien dorée, croustillante à l’extérieur, moelleuse à l’intérieur, une véritable baguette 100% fonctionnelle. Vous en salivez déjà.&lt;/p&gt;

&lt;p&gt;Et maintenant, si je vous raconte que j’ai surpris le boulanger la main dans le froc en train de se gratter l’anus pendant qu’il pétrissait la pâte. Toujours envie de vous en faire des tartines ?&lt;/p&gt;

&lt;p&gt;Livrer une baguette seulement &lt;em&gt;fonctionnelle&lt;/em&gt; est-ce vraiment tout ce qu’on demande à un artisan boulanger ? Non, on exige de lui qu’il prépare son pain dans les règles de l’art, en respectant les bonnes pratiques de son métier et en suivant scrupuleusement les règles sanitaires.&lt;/p&gt;

&lt;p&gt;Si je vous raconte qu’en cette période de crise covid19, votre boulanger travaille sans porter de masque et en éternuant allégrement sur la pâte des croissants qu’il est en train de préparer. Vous voyez la scène ? Un gros &lt;em&gt;ATCHOUM&lt;/em&gt; accompagné d’un feu d’artifice de postillons et de sécrétions nasales qui viennent se déposer gaiement un peu partout dans la pâte. Mmmm, &lt;em&gt;un délice&lt;/em&gt;. Vous en pensez quoi ? Sérieusement ? C’est professionnel comme comportement ?&lt;/p&gt;

&lt;p&gt;Ça vous dit un croissant totalement fonctionnel aromatisé à la crotte de nez ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F30%2Fcroissants.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F30%2Fcroissants.jpg" alt="Croissants dans un panier - Image par Pexels de Pixabay" width="800" height="449"&gt;&lt;/a&gt;Ce serait dommage d'y trouver des crottes de nez, n'est-ce-pas ?&lt;/p&gt;

&lt;p&gt;Bien entendu, vous avez parfaitement le droit de répondre oui et de trouver que c’est normal de travailler comme ça tout en prétendant être un professionnel du métier. Mais je n’irai pas manger chez vous ni vous recommander auprès de mes clients !&lt;/p&gt;

&lt;p&gt;Parce qu’un produit fonctionnel, c’est juste le B-A-BA, c’est le niveau zéro du métier. C’est ce qu’on demande de produire à un étudiant pour son projet de première année. Juste un truc qui marche. Mais être professionnel, c’est un peu plus que ça non ? Non !?&lt;/p&gt;

&lt;h1&gt;
  
  
  Artisan développeur
&lt;/h1&gt;

&lt;p&gt;Bien entendu, je ne prétends pas que tout est toujours parfait, peut-être le boulanger va-t-il y laisser un poil de bras ou une goutte de sueur. On ne peut pas tout prévoir, on n’est pas toujours au top. Parfois aussi on se trompe, c’est humain. Je vous le dis discrètement, ça reste entre nous, mais on n’a pas non plus la science infuse. Parfois on ne sait pas bien faire et on aurait pu faire mieux.&lt;/p&gt;

&lt;p&gt;Alors quoi ?&lt;/p&gt;

&lt;p&gt;Alors, on prend soin de respecter au mieux les bonnes pratiques, on se forme, on s’exerce, on ne se cache pas derrière un &lt;em&gt;de toute façon le client attend juste que ça fonctionne&lt;/em&gt;. On se comporte comme un véritable artisan du code.&lt;/p&gt;

&lt;p&gt;C’est-à-dire que s’il y a des bugs, c’est accidentel. Si le code lève une &lt;em&gt;exception&lt;/em&gt;, c’est parce qu’on a rencontré un cas &lt;em&gt;exceptionnel&lt;/em&gt; (le terme d’&lt;em&gt;exception&lt;/em&gt; n’a pas été choisi par hasard).&lt;/p&gt;

&lt;p&gt;Un bug issu de &lt;em&gt;l’erreur humaine&lt;/em&gt; est acceptable. Un bug issu de &lt;em&gt;non mais ce n’est pas la peine de coder proprement il suffit juste que ça fonctionne&lt;/em&gt; est juste inacceptable. Vous saisissez la nuance ?&lt;/p&gt;

&lt;p&gt;Autrement dit, on évite autant que possible d’éternuer sur le clavier et de laisser des crottes de nez partout dans le code.&lt;/p&gt;

&lt;h1&gt;
  
  
  C’est quoi une crotte de nez dans le code ? Il est où le problème ?
&lt;/h1&gt;

&lt;p&gt;Je pourrais écrire une thèse sur le sujet mais, que vous soyez développeur ou que vous ayez un projet de développement, je vous recommande plutôt la lecture du fantastique &lt;a href="https://amzn.to/3gj03ST" rel="noopener noreferrer"&gt;The Pragmatic Programmer, from journeyman to master&lt;/a&gt; de Andrew Hunt et David Thomas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://amzn.to/3gj03ST" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq3hx0v0r875n6uv4kqo.jpg" width="800" height="1018"&gt;&lt;/a&gt;The Pragmatic Programmer, from journeyman to master&lt;/p&gt;

&lt;p&gt;Ce livre qui date de plus d’une vingtaine d’années reste complètement d’actualité tant les thèmes qu’il aborde sont tranverses au développement informatique et se basent avant tout sur les bonnes pratiques et surtout les risques de ne pas les suivre.&lt;/p&gt;

&lt;p&gt;Vous y apprendrez par exemple pourquoi en vertu de &lt;em&gt;&lt;a href="https://fr.wikipedia.org/wiki/Hypoth%C3%A8se_de_la_vitre_bris%C3%A9e" rel="noopener noreferrer"&gt;l’hypothèse de la vitre brisée&lt;/a&gt;&lt;/em&gt;, si un développeur laisse traîner quelques crottes de nez par-ci par-là, le code finira irrémédiablement recouvert de sécrétions nasales.&lt;/p&gt;

&lt;p&gt;Et en informatique, une crotte de nez peut avoir des répercussions graves.&lt;/p&gt;

&lt;p&gt;En fait, une crotte de nez dans le code, c’est ce truc-là, qu’on ne voit pas de l’extérieur, qui ne semble pas influer sur le fonctionnel du point de vue de l’utilisateur mais qui, dans certains cas, laisse un arrière-goût plutôt désagréable.&lt;/p&gt;

&lt;p&gt;Comme oublier de crypter des mots de passe. Je veux dire, au niveau du code, ce n’est pas grand-chose, hein (ah si ?). On oublie juste de passer le mot de passe dans la moulinette, rien de très grave en soit, personne ne s’en rend compte, tout est parfaitement fonctionnel. Et puis, un beau jour, c’est le scandale…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.commentcamarche.net/news/5872520-facebook-stockait-des-centaines-de-millions-de-mots-de-passe-en-clair" rel="noopener noreferrer"&gt;Facebook stockait des millions de mot de passe instagram en clair&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cnetfrance.fr/news/oups-google-a-stocke-en-clair-des-mots-de-passe-durant-des-annees-39884993.htm" rel="noopener noreferrer"&gt;Petite crotte de nez chez Google : des mots de passe stockés en clair pendant des années&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;J’ai une autre anecdote sympa, c’est cocasse vous allez voir. J’ai assisté à une scène surréaliste ou un développeur a voulu montrer à un opérateur comment supprimer des données dans la base clients. Jusque-là, rien de dramatique, on manipule des données, on les insère, on les modifie, on les supprime, c’est la raison d’être des bases de données.&lt;/p&gt;

&lt;p&gt;Sauf qu’il a, par mégarde, sélectionné l’ensemble des données d’une table. Et que cette table avait une relation directe ou indirecte avec l’ensemble de toutes les autres tables de la base de données.&lt;/p&gt;

&lt;p&gt;Vous voyez le coup venir ? Une crise d’éternuement en approche !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F30%2Fatchoum-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.sylvainmoingeon.fr%2Fmedia%2Fposts%2F30%2Fatchoum-2.jpg" alt="Femme sur le point d'éternuer" width="800" height="533"&gt;&lt;/a&gt;Noooon, pas dans le code !&lt;/p&gt;

&lt;p&gt;Dans le meilleur des mondes, il aurait dû y avoir une boite de dialogue de confirmation de suppression au niveau applicatif. Il aurait dû y avoir un contrôle au niveau de la base de données, en particulier sur une table critique comme celle-ci.&lt;/p&gt;

&lt;p&gt;Mais voilà, la boite de dialogue est restée engluée dans une flaque de morve et la base de donnée a &lt;strong&gt;supprimé toutes les données en cascade&lt;/strong&gt; sans broncher.&lt;/p&gt;

&lt;p&gt;Quand le développeur a demandé, l’espoir dans l’âme, &lt;em&gt;Ce n’est rien, nous allons restaurer les données, vous avez bien une sauvegarde récente ?&lt;/em&gt;, l’opérateur a constaté que les scripts de sauvegarde ne tournaient plus depuis des semaines, sans envoyer les messages d’avertissement prévus dans ce cas. La faute à une crotte de nez. Encore.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Être professionnel, ce n’est pas seulement produire du code fonctionnel, c’est aussi garantir une &lt;strong&gt;qualité&lt;/strong&gt; , une &lt;strong&gt;pérennité&lt;/strong&gt; , une &lt;strong&gt;évolutivité&lt;/strong&gt; du code. C’est-à-dire un code qui fonctionne aujourd’hui et continuera de fonctionner demain, même après modification.&lt;/p&gt;

&lt;p&gt;Je pourrais également parler de &lt;strong&gt;stabilité&lt;/strong&gt; (ça fonctionne mais c’est un peu bancal), de &lt;strong&gt;fiabilité&lt;/strong&gt; (ça fonctionne très bien… quand ça fonctionne), ou de &lt;strong&gt;performance&lt;/strong&gt; (ça fonctionne mais il faut être patient, très patient).&lt;/p&gt;

&lt;p&gt;Ou encore, insister sur la &lt;strong&gt;lisibilité&lt;/strong&gt; d’un code que d’autres développeurs n’auront pas de mal à lire et à comprendre. D’un code qui &lt;em&gt;coule de source&lt;/em&gt; (ah ah ah) dans lequel les autres n’auront pas peur de mettre les mains. J’ai failli dire les doigts… dans le nez, bien entendu.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code toujours comme si la personne qui va maintenir ton code est un violent psychopathe qui sait où tu habites.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;John F. Woods&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alors évidemment, comme je l’ai dit, on ne pas toujours être au top, on va parfois laisser quelques gouttes de sueurs perler dans la farine, le pain sera un peu moins cuit ou la pâte moins croustillante que d’habitude. C’est comme ça, c’est la vie.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mais s’il vous plait, cessez de vous gratter le cul ou de vous curer le nez en bossant sous prétexte que ça ne va pas se voir !&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>craftsmanship</category>
      <category>bestpractice</category>
      <category>cleancode</category>
      <category>french</category>
    </item>
  </channel>
</rss>
