<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <title>Sylvain Moingeon</title>
    <link href="https://www.sylvainmoingeon.fr/feed.xml" rel="self" />
    <link href="https://www.sylvainmoingeon.fr" />
    <updated>2025-06-11T15:15:02+02:00</updated>
    <author>
        <name>Sylvain</name>
    </author>
    <id>https://www.sylvainmoingeon.fr</id>

    <entry>
        <title>Lettre aux timides et introverti(e)s</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/lettre-aux-timides-et-introverties/"/>
        <id>https://www.sylvainmoingeon.fr/lettre-aux-timides-et-introverties/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/51/Gemini_Generated_Image_k0zt3ak0zt3ak0zt.jpeg" medium="image" />
            <category term="softskills"/>

        <updated>2025-06-10T11:31:28+02:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/51/Gemini_Generated_Image_k0zt3ak0zt3ak0zt.jpeg" alt="" />
                    On te dit réservé, introverti ou, pire :&nbsp;timide ? On te le&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/51/Gemini_Generated_Image_k0zt3ak0zt3ak0zt.jpeg" class="type:primaryImage" alt="" /></p>
                
  <p>
    On te dit réservé, introverti ou, pire :&nbsp;<i>timide </i>? On te le reproche, comme si c'était une tare, et que, de surcroît, tu le faisais exprès ? Tu as déjà entendu ce discours lors de ta scolarité :
  </p>

    <blockquote class="blockquote">
      Tu pourrais réussir, dommage que tu sois si timide<br>
    </blockquote>

  <p>
    Tu te sens dénigré par toute une partie de la société qui ne jure que par l'extravagance, qui t'explique à longueur de journée que pour exister il faut s'exprimer, plus fort que l'autre si possible.
  </p>

  <p>
    On te l'a tellement dit que tu y crois, tu n'arriveras jamais à rien, ta parole n'a pas d'importance. Et de toute façon qui t'écouterait ?
  </p>

  <p>
    Cet article est pour toi !
  </p>

    <h2 id="pourquoi-jen-parle-maintenant">
      Pourquoi j'en parle maintenant ?
    </h2>

  <p>
    Parce que.
  </p>

  <p>
    Ce qui en soit est déjà une très bonne raison. Je veux dire par là que j'entends autour de moi tout le monde donner son avis sur tout, en particulier quand il n'y a aucune légitimité à le faire. Donc pour une fois, moi aussi, je l'ouvre.
  </p>

  <p>
    Mais il y a d'autres éléments. Une certaine mentalité dans le système scolaire qui semble ne pas avoir évolué en plus de 30 ans. Sur la question de la participation orale en classe, notamment. Pour faire simple, si tu es discret tu es catalogué <i>timide, et c'est dommage parce que sinon tu pourrais réussir</i>. Comme si l'introversion était à elle seule une cause d'échec totale de ta vie.
  </p>

  <p>
    Il y a aussi les processus de recrutement. Le CV est trop succinct pour refléter réellement ton parcours (15 ans de carrière en une page aérée, la bonne blague) et l'entretien reste un exercice façonné pour les extravertis qui aiment s'exprimer et qui ont du bagou. Un candidat qui aime parler de lui me damera le pion sans effort.
  </p>

  <p>
    Je suis introverti, j'ai un profil d'observateur, je prend du recul, de la mesure et je n'agis ni ne parle sans avoir bien réfléchi en amont. Je peux donc facilement donner l'impression d'être sur la réserve, voire dans l'inaction. Parfois d'être un suiveur ou en tout cas de ne pas être celui qui pousse les autres en avant.
  </p>

  <p>
    Et pourtant !
  </p>

  <p>
    Si tu es timide, introverti et que tu penses que la réussite c'est pour les autres, lis ce qui suit.
  </p>

  <p>
    Car oui, je suis introverti mais…
  </p>

    <h2 id="je-sais-entreprendre">
      Je sais entreprendre
    </h2>

  <p>
    Malgré mon attitude nonchalante et l'impression que je donne, parfois, de ne pas en glander une, j'aurai au cours de ces dernières années créé deux entreprises, monté une association sportive dans laquelle je donne des cours d'arts-martiaux, suivi une formation de lutherie en guitare, fabriqué plusieurs instruments et participé à plusieurs salons de lutherie en tant qu'exposant.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/51/20241101_122430-2.jpg" height="4624" width="3468" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/51/responsive/20241101_122430-2-xs.jpg 300w ,https://www.sylvainmoingeon.fr/media/posts/51/responsive/20241101_122430-2-sm.jpg 480w ,https://www.sylvainmoingeon.fr/media/posts/51/responsive/20241101_122430-2-md.jpg 768w ,https://www.sylvainmoingeon.fr/media/posts/51/responsive/20241101_122430-2-lg.jpg 1024w">
      <figcaption>Pendant que je ne parle pas, je fabrique des guitares</figcaption>
    </figure>

  <p>
    Pour chacune de ces activités, j'ai tout géré de A à Z : la création, la gestion, la communication, le site web, les réseaux sociaux, la publicité, les relations clients et relations publiques…
  </p>

  <p>
    J'avance à mon rythme mais j'avance, là où d'autres s'agitent, donnent l'impression de beaucoup s'activer pour finalement rester sur place.
  </p>

  <p>
    Si tu es assez vieux, tu te souviens peut-être de ce slogan de publicité :
  </p>

    <blockquote class="blockquote">
      Ce sont ceux qui en parlent le moins qui en mangent le plus<br>
    </blockquote>

  <p>
    <b>Souviens toi, ce n'est pas parce que tu en parles peu que tes actions ont moins de valeur.</b>
  </p>

    <h2 id="je-suis-federateur-bon-un-peu">
      Je suis fédérateur (bon, un peu)
    </h2>

  <p>
    Dans l'imaginaire collectif, le charismatique beau gosse fort en gueule est entouré d'une foule en délire prête à le suivre jusqu'à la mort.
  </p>

  <p>
    Sérieusement, tu as bien regardé Bill Gates, Mark Zuckerberg ou Steve Jobs ? Des types sur lesquels personne ne se retournerait dans la rue. Et au moins trois d'entre eux n'ont jamais eu vraiment l'air dans leur élément lorsqu'il s'agit de s'exprimer en public.
  </p>

  <p>
    Comme je l'ai mentionné plus haut, je donne des cours d'arts-martiaux. Pendant la séance, on écoute ce que j'ai à dire, de 14 à 63 ans.
  </p>

  <p>
    Je n'ai pas besoin d'être charismatique, ni d'imposer ma parole. Juste de montrer par l'exemple que ce que j'ai à dire vaut la peine d'être entendu.
  </p>

  <p>
    Quand ce que tu montres a de la valeur, tu n'as pas besoin de justifier de ta valeur.
  </p>

  <p>
    Je ne prétends pas que c'est facile, j'ai mis du temps à être à l'aise sur le tatami devant un public, mais même si on t'a dit le contraire toute ta vie, tu peux être introverti,&nbsp; transmettre un message et être écouté.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/51/WhatsApp-Image-2025-06-05-at-20.35.19-1.jpeg" height="1536" width="2048" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/51/responsive/WhatsApp-Image-2025-06-05-at-20.35.19-1-xs.jpeg 300w ,https://www.sylvainmoingeon.fr/media/posts/51/responsive/WhatsApp-Image-2025-06-05-at-20.35.19-1-sm.jpeg 480w ,https://www.sylvainmoingeon.fr/media/posts/51/responsive/WhatsApp-Image-2025-06-05-at-20.35.19-1-md.jpeg 768w ,https://www.sylvainmoingeon.fr/media/posts/51/responsive/WhatsApp-Image-2025-06-05-at-20.35.19-1-lg.jpeg 1024w">
      <figcaption>de 14 à 63 ans, on m'écoute religieusement…</figcaption>
    </figure>

  <p id="je-fais-bouger-les-lignes">
    Le plus appréciable, c'est qu'en cas de besoin, ils sont là. Si je participe à une démonstration ou un événement quelconque, je n'ai pas besoin de taper du pied, ils m'accompagnent. Ça parait anodin, mais loin d'être une évidence, tant il devient difficile de dessouder les fesses des gens de leur canapé.
  </p>

  <p>
    <b>Oui, tu peux être le <i>timide de la classe</i> et pourtant devenir l'élément moteur d'un groupe.</b>
  </p>

    <h2 id="je-fais-bouger-les-lignes">
      Je fais bouger les lignes
    </h2>

  <p>
    J'avoue, l'exemple précédent présente un biais. Je suis le prof, donc forcément, on m'écoute. Mais même quand je ne parle pas, on m'écoute.&nbsp;
  </p>

  <p>
    Comment s'exécute ce prodige ? Voyons un exemple.
  </p>

  <p>
    Lorsque j'étais Freelance, j'ai été contacté pour une mission d'urgence. Une application mobile qui explosait littéralement de tous les côtés. On m'a bien expliqué :
  </p>

    <blockquote class="blockquote">
      On n'a pas le temps de faire bien, on doit avancer vite !
    </blockquote>

  <p>
    En substance, deux développeurs faisaient évoluer l'application, j'étais chargé de colmater les brèches.
  </p>

  <p>
    Très vite, j'ai signalé la base branlante sur laquelle était construite l'application. Du couplage partout, une violation permanente des bonnes pratiques, un irrespect total de l'architecture du projet, bref un vrai bordel. J'ai osé suggérer qu'on refactorise, qu'on nettoie le code : je me suis pris une levée de boucliers.
  </p>

  <p>
    Puis on m'a confié une fonctionnalité à développer. J'ai estimé quatre jours de travail, on m'en a donné deux. Parce qu'on n'a pas le temps de faire propre. Déconne pas, hein, tu vas pas perdre du temps à faire les choses bien quand même ?
  </p>

  <p>
    Quatre jours plus tard, donc, je publie mon code. Propre. Découplé des autres fonctionnalités. En respectant les bonnes pratiques. Parce que parfois, je suis un vrai rebelle.&nbsp;Et que la parole ne suffit pas toujours.
  </p>

  <p>
    Je me souviens avoir lu, mais je ne sais plus où :
  </p>

    <blockquote class="blockquote">
      Il vaut mieux faire et présenter ses excuses après, plutôt que demander l'autorisation, parce que l'autorisation, tu ne l'auras jamais.<br>
    </blockquote>

  <p>
    Alors bien sûr, ça a râlé. J'ai dit <i>désolé</i>. En vrai, je n'étais pas désolé du tout.
  </p>

  <p>
    C'est parti en test. Je n'ai pas eu de nouvelles pendant une semaine. J'ai cru que j'étais cuit.
  </p>

  <p>
    Puis le téléphone sonne, le CTO de la boite :&nbsp;
  </p>

    <blockquote class="blockquote">
      — Ça fait une semaine qu'on teste ce que tu as livré, on n'a aucun crash, aucun bug de régression, juste une petite correction cosmétique à apporter et c'est prêt à être livré.<br>— Bah oui<br>
    </blockquote>

  <p>
    Puis après le temps d'un soupir :
  </p>

    <blockquote class="blockquote">
      — on peut savoir comment tu as fait ?<br>—<b>&nbsp;J'ai pris quatre jours.</b><br>
    </blockquote>

  <p>
    Les deux autres développeurs ont été remerciés, <b>j'ai eu le feu vert pour remettre de l'ordre dans l'application. </b>Plus tard, j'ai été embauché en CDI dans la boite.
  </p>

  <p>
    Tu ne seras peut-être jamais un grand orateur, mais tu as d'autres moyens pour convaincre. La démonstration par l'exemple en est un. Cela nécessite juste un peu de courage. Mais si tu as survécu dans notre monde en étant timide, j'imagine que tu ne dois pas en manquer.
  </p>

    <h2 id="je-mentoure-dun-cercle-vertueux">
      Je m'entoure d'un cercle vertueux
    </h2>

  <p>
    Oh, ça parait bien prétentieux ça, encore plus que tout ce qui précède. Mais ce n'est pas moi qui le dit, ni ma maman, alors ça doit être vrai.
  </p>

  <p>
    Il y a quelques jours, j'étais justement reconnaissant d'être entouré de personnes bienveillantes, volontaires, toujours prêtes à me donner un coup de main. Et ma compagne, surprise, m'a répondu en substance :
  </p>

    <blockquote class="blockquote">
      Tu n'avais jamais remarqué ? Quoi que tu fasses, tu finis toujours entouré de gens bien.
    </blockquote>

  <p>
    Alors j'ai pris quelques instants de réflexion, et avec un peu de recul… c'est pas faux.
  </p>

  <p>
    Je ne sais pas à quoi cela tient. Un super-pouvoir ? Un talent caché ? Ou simplement ma façon d'être ? Ma capacité à feindre l'indifférence face à ce qui m'emmerde ? Parce qu'entre nous, ceux qui cherchent l'embrouille, leur raison d'être c'est qu'on les remarque. Moi, je les ignore et concentre mes efforts vers ceux qui me tire vers le haut.
  </p>

  <p>
    Aussi loin que je me souvienne, je me suis toujours entouré de personnes que je considérais comme "meilleures" que moi. Mes amis ont toujours été des personnes brillantes dans leur domaine. Gamin, au Judo, je travaillais toujours avec plus grand, plus fort ou meilleur combattant que moi. Cela fait remonter en moi le souvenir d'un de mes professeurs d'arts-martiaux. Il disait :
  </p>

    <blockquote class="blockquote">
      Les gens qui ne m'intéressent pas, je les dégage.
    </blockquote>

  <p>
    Un peu brutal, certes. Et si mon point de vue est plus nuancé, j'imagine que ses paroles ont fait écho dans mon esprit et m'ont inspiré durant ma vie.
  </p>

  <p>
    Si des cons te dérangent, si certains ne tolèrent pas ta réserve, se moquent de ta timidité : dégage-les de ta vie. Si tu ne peux pas : dégage-les de ta vie. Il y a toujours un moyen. Ne serait-ce qu'en s'entourant de gens bienveillants. <b>Les gens bien, ça fait fuir les cons, c'est même à ça qu'on les reconnait.</b>
  </p>

    <h2 id="vraiment-de-la-timidite">
      Vraiment de la timidité ?
    </h2>

  <p>
    Pour finir, je vais te poser cette question étrange : es-tu réellement timide ? Ou l'es-tu devenu par injonction, parce qu'on te l'a répété encore et encore ?
  </p>

  <p>
    Il arrive régulièrement que je patine à l'oral, que je bafouille ou donne l'impression de ne pas savoir quoi répondre. La vérité c'est que <b>je me sens étriqué dans l'expression orale</b>.
  </p>

  <p>
    D'abord, celle-ci peine à refléter ce que j'ai réellement à l'esprit. Tu as peut-être remarqué, mes articles de blog sont souvent un peu longs et développés. Dans ma tête c'est pareil : je n'ai pas de réponse préétablie ou d'idée reçue à ressortir à volonté. Quel que soit la question qu'on me pose, j'ai immédiatement un arbre de réflexion complet qui se développe. Et celui-ci peut varier d'un instant à l'autre en fonction des éléments qu'on me fournit. Je révise mon jugement en permanence. Parfois même en temps réel alors que je m'exprime, parce que la phrase que je viens de prononcer m'a ouvert une nouvelle piste de réflexion. <b>S'exprimer à l'oral devient alors un enfer</b>.
  </p>

  <p>
    Ensuite, et c'est plus personnel, j'ai souvent l'impression de ne pas intégrer le même système d'exploitation que mes semblables. J'ai du mal à saisir leurs intentions, je ne les comprends pas. J'ai appris à le faire au cours du temps, mais ce n'est pas naturel pour moi. Certaines choses paraissent évidentes à tout le monde mais me laissent perplexe. Par exemple, comment peut on avoir un chiffre préféré ? A chaque fois qu'on me pose la question, je me noie dans un océan de perplexité. Je suis le malheur des numérologues.
  </p>

    <blockquote class="blockquote">
      — Allez donne moi tes trois chiffres préférés<br>— Je n'ai pas de jugement de valeur concernant les chiffres<br>— Non mais tes chiffres porte bonheur quoi !<br>— Je ne suis réellement pas convaincu qu'un chiffre puisse porter chance<br>— Non mais t'es con ou quoi ? Il y a bien des chiffres qui font plus de sens pour toi ?<br>— Si tu insistes, le nombre π, la constante de Planck et le nombre d'Avogadro<br>— Mais j'ai pas ça dans mon tarot !<br>
    </blockquote>

  <p>
    Je ne sais pas non plus parler de la pluie et du beau temps. Je comprends bien qu'il s'agit d'une norme sociale mais je n'arrive pas à m'y faire. Oui, j'ai bien vu qu'il fait beau, je ne suis pas benêt, c'est même pour ça que je porte une casquette et des lunettes de soleil. On est vraiment obligé de se faire un point météo ?
  </p>

  <p>
    Bref, parfois je ne parle pas, c'est juste que la conversation ne m'intéresse pas. Ou que je n'ai pas de point fondamentalement plus intéressant à y apporter. Et bizarrement, ne pas parler quand on n'a rien à dire, ça gène les gens.
  </p>

  <p>
    Alors, depuis que je suis petit on me répète que je suis timide. La vérité c'est que la plupart du temps, je trouve les questions qu'on me pose absurdes. Ou à l'inverse qu'elles nécessiteraient un essai pour y répondre convenablement, l'expression orale ne s'y prêtant pas.
  </p>

  <p>
    Et pire, les réponses qu'on y donne sont rarement valables. Trop succinctes pour refléter la réalité, trop binaires pour y apporter la nuance nécessaire. Mais il semblerait que la plupart de l'humanité fonctionne ainsi. Pas moi. Et ça m'épuise. Au sens propre. Alors <b>je parle peu et je choisis scrupuleusement mes interlocuteurs et les discussions dans lesquelles je m'engage</b>. Question de survie.
  </p>

    <h2 id="et-toi">
      Et toi ?
    </h2>

  <p>
    Tu te considères timide, introverti ou on te décrit comme tel. Mais quelle réalité se cache derrière ces mots ? Pourquoi as-tu du mal à t'exprimer ? La peur de parler ? Celle d'être mal compris ? La confusion dans ta tête ? Ou juste parler t'épuise ?
  </p>

  <p>
    Je suis curieux d'avoir ton avis sur le sujet : à quel point ça t'handicape dans ta vie ? quels autres moyens ou alternatives as-tu trouvé pour t'exprimer ? As-tu à l'inverse réussi à en faire une force, en tirer un avantage ? Quels super-pouvoirs cachent ta timidité ?
  </p>

  <p>
    Je te laisse le temps de la réflexion, mais ta réponse m'intéresse tellement que j'ai même réactivé le système de commentaire du site ! Si tu ne le vois pas, active le cookie dans la bannière (bouton en bas à gauche).
  </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>CV - Développeur senior tout terrain</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/cv-developpeur-informatique-sylvain-moingeon-dijon/"/>
        <id>https://www.sylvainmoingeon.fr/cv-developpeur-informatique-sylvain-moingeon-dijon/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/47/CV_tout_terrain_ChatGPT.png" medium="image" />
            <category term="software craftsmanship"/>
            <category term="ioc"/>
            <category term="développeur informatique"/>
            <category term="bonnes pratiques"/>
            <category term="YAGNI"/>
            <category term="Xamarin.Forms"/>
            <category term="XAML"/>
            <category term="SOLID"/>
            <category term="Pragmatic Programmer"/>
            <category term="MVVM"/>
            <category term="MAUI"/>
            <category term="KISS"/>
            <category term="Emploi"/>
            <category term="Dijon"/>
            <category term="DRY"/>
            <category term="Clean Code"/>
            <category term="C#"/>
            <category term="Azure"/>

        <updated>2025-03-31T15:14:31+02:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/47/CV_tout_terrain_ChatGPT.png" alt="" />
                    
    <blockquote id="telecharger-le-cv-pdf" class="blockquote">
      <a href="https://www.sylvainmoingeon.fr/media/files/CV Développeur - sylvain moingeon.pdf" target="_blank" download>Télécharger mon CV en PDF</a>&nbsp;
    </blockquote>

    <h2 id="resume-et-un-peu-plus">
      Résumé (et un peu plus)
    </h2>

  <p>
    J'ai débuté ma carrière informatique en 2006 après un cursus scientifique universitaire (BAC+5).
  </p>

  <p>
    Durant cette période, j'ai porté de nombreuses casquettes : développeur, hotliner, formateur, encadrant, rédacteur, testeur. Et j'ai suivi l'évolution des langages et outils de développement, principalement chez Microsoft : <b>VB6</b>, <b>C#</b>, <b>WinForms</b>, <b>WPF</b>, <b>UWP</b>,&nbsp; <b>Xamarin.Forms</b>, .Net <b>MAUI</b>.<br>
  </p>

                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/47/CV_tout_terrain_ChatGPT.png" class="type:primaryImage" alt="" /></p>
                
    <blockquote id="telecharger-le-cv-pdf" class="blockquote">
      <a href="https://www.sylvainmoingeon.fr/media/files/CV Développeur - sylvain moingeon.pdf" target="_blank" download>Télécharger mon CV en PDF</a>&nbsp;
    </blockquote>

    <h2 id="resume-et-un-peu-plus">
      Résumé (et un peu plus)
    </h2>

  <p>
    J'ai débuté ma carrière informatique en 2006 après un cursus scientifique universitaire (BAC+5).
  </p>

  <p>
    Durant cette période, j'ai porté de nombreuses casquettes : développeur, hotliner, formateur, encadrant, rédacteur, testeur. Et j'ai suivi l'évolution des langages et outils de développement, principalement chez Microsoft : <b>VB6</b>, <b>C#</b>, <b>WinForms</b>, <b>WPF</b>, <b>UWP</b>,&nbsp; <b>Xamarin.Forms</b>, .Net <b>MAUI</b>.<br>
  </p>


  <p>
    Lors de mon premier emploi, j'ai acquis de solides connaissances concernant la <b>géomatique </b>avec l'usage d'outils de <b>cartographie </b>tels que <b>MapInfo</b>, la programmation de script <b>MapBasic</b>, ou encore la manipulation de carte au format <b>ESRI Shape</b>. J'y ai travaillé sur des projets fortement <b>orientés métiers</b> (infrastructures routières, concessions d'autoroutes ou collectivités locales) et, notamment, j'ai assuré la maintenance corrective et évolutive, ainsi que la hotline d'une solution installée chez plus de 2000 clients. Autant dire que c'est formateur !
  </p>

  <p>
    Saisissant une opportunité, je me suis ensuite lancé en tant qu'indépendant, en faisant le choix, en accord avec mes principes, de ne travailler qu'en direct avec les clients. Au cours de cette période, je me suis initié au <b>développement mobile</b> (iOS et Android), travaillant majoritairement avec des start-ups. A cette occasion, j'ai remis sur les rails un projets qui partait droit dans le mur. Contacté pour <i>corriger des bugs de régression</i>, j'ai découvert un <i>code spaghetti difficilement maintenable</i>. A force de persuasion, j'ai fini par convaincre l'équipe de ralentir la cadence, travailler proprement, et après une <b>refactorisation </b>progressive du code, rendu le <b>projet stable et maintenable</b>. Un autre projet en <b>recherche et développement</b>, m'a amené à manipuler des technologies un peu plus avancées : reconnaissance faciale, communication ultra-son, bluetooth LE, geofencing…
  </p>

  <p>
    Au cours de la période COVID, mon principal client me propose une embauche en CDI que j'accepte devant l'incertitude de la situation. Jusqu'en juin 2023 où essuyant plusieurs revers, la start-up cesse son activité. Le nez dans le guidon, je décide alors de prendre du recul sur ma carrière et de faire une pause, une période sans écran ! Je me lance dans une formation en facture instrumentale, un vieux rêve qui restait à réaliser.
  </p>

  <p>
    Aujourd'hui, la tech me rappelle, mon intérêt éveillé par les progrès fulgurant de l'intelligence artificielle ces deux dernières années. On ne se refait pas. Geek un jour, geek toujours.
  </p>

  <p>
    Me voici de retour.
  </p>

    <h2 id="etat-desprit">
      État d'esprit
    </h2>

  <p>
    En 15 ans de carrière, j'ai appris une vérité fondamentale : l'état d'esprit et le respect des bonnes pratiques sont garantes du succès d'un projet de développement informatique, bien plus que la technique.
  </p>

  <p>
    Si vous cherchez un développeur capable de résoudre des tests techniques les yeux fermés, connaissant l'intégralité d'un framework sans jamais avoir recours à la documentation, je ne suis pas votre homme. Et ça ne m'intéresse pas. Ce n'est pas mon métier.
  </p>

  <p>
    Si vous cherchez un développeur capable de <b>mener à bien un projet</b>, d'établir d <b>bonnes pratiques</b> pour le rendre <b>stable</b>, fiable et sans bugs de régression, alors <a href="https://www.sylvainmoingeon.fr/contact/">contactez moi</a>.
  </p>

    <h2 id="competences">
      Compétences
    </h2>

    <h3 id="bonnes-pratiques-et-clean-code">
      Bonnes pratiques et clean code
    </h3>

  <ul>
    <li>Responsabilité unique</li><li>Pureté des fonctions</li><li>DRY</li><li>KISS</li><li>YAGNI</li><li>SOLID</li><li>Boy scout rule</li><li>Donner des noms explicites aux classes, méthodes et arguments</li><li>Pas de code mort</li><li>Pas de code mis en commentaire</li><li><span style="color: var(--text-primary-color); font-family: var(--editor-font-family); font-size: 1em; font-weight: var(--font-weight-normal);">Pas de "nombre magique"</span><br></li><li>Pas de chaine de texte en dur</li><li>Commenter uniquement le nécessaire (même si ça fait débat)<br></li><li>Refactoriser / laisser émerger le design</li><li>Tests unitaires</li>
  </ul>

    <h3 id="connaissances-techniques">
      Connaissances techniques
    </h3>

    <h4 id="langages-et-outils-de-developpement">
      Langages et outils de développement
    </h4>

  <ul>
    <li>C#, XAML, VB6, MapBasic</li><li>Xamarin.Forms , MAUI (android &amp; iOS) / UWP / WPF / Winforms<br></li><li>Visual Studio / DevExpress / Syncfusion / .Net</li><li>Git / Github / Azure DevOps</li><li>LINQ<br></li><li>Inno Setup</li>
  </ul>

    <h4 id="base-de-donnees-et-modelisation">
      Base de données et modélisation
    </h4>

  <ul>
    <li>Oracle / SQL Server</li><li>Entity Framework / ADO .Net</li><li>SQL</li><li>Merise</li>
  </ul>

    <h4 id="technologies-avancees">
      Technologies avancées
    </h4>

  <ul>
    <li>Reconnaissance faciale (framework Rank One Face Recognition)</li><li>Communication ultra-son (framework LISNR)</li><li>Géorepérage / geofencing (détection d'entrée/sortie de zone géographique)</li><li>Bluetooth LE<br></li>
  </ul>

    <h4 id="geomatique-sig-et-cartographie">
      Géomatique, SIG et cartographie
    </h4>

  <ul>
    <li>MapInfo</li><li>MapBasic</li><li>MapX</li><li>MapWinGis</li><li>ESRI Shape</li><li>Plans cadastraux informatisés</li>
  </ul>

    <h3 id="competences-transverses">
      Compétences transverses
    </h3>

  <ul>
    <li>Support technique / hotline</li><li>Formation utilisateur</li><li>Relation client</li><li>Encadrement</li><li>Rédaction de documents (cahier de recettes, manuel utilisateur…)</li><li>Gestion de micro-entreprise</li><li>Communication numérique / réseaux sociaux</li>
  </ul>

    <h3 id="connaissances-metiers">
      Connaissances métiers
    </h3>

  <ul>
    <li><span style="color: var(--text-primary-color); font-family: var(--editor-font-family); font-size: 1em; font-weight: var(--font-weight-normal);">Concessionnaires d’autoroutes / infrastructure routière</span><br></li><li>Collectivités locales, cadastre, urbanisme, cimetière<br></li>
  </ul>

    <h3 id="personnalite">
      Personnalité
    </h3>

  <ul>
    <li>Curiosité intellectuelle</li><li>Rigueur</li><li>Esprit critique</li><li>Capacité à s'autoformer</li><li>Goût du travail bien fait</li><li>Discrétion</li>
  </ul>

    <h2 id="experiences-professionnelles">
      Expériences professionnelles
    </h2>

    <h3 id="developpeur-net-c-xamarinforms-maui-cddcdi">
      Développeur .Net C#, Xamarin.Forms, Maui (CDD/CDI)
    </h3>

  <p>
    <b><i>Octobre 2021 - juin 2023 : ESTAY (Aix-en-Provence, télétravail 100%)</i></b>
  </p>

  <p>
    Développement d'un service B2B de mutualisation de trajets/covoiturage professionnel. L'enjeu était d'économiser sur les transports et réduire l'impact carbone d'une entreprise en détectant les trajets professionnels pouvant être mutualisés entre les lieux d'intérêts (gares, aéroports, hôtel…).
  </p>

  <p>
    L'application mobile permettait aux usagers d'entrer en contact et d'organiser leurs déplacements.
  </p>

  <p>
    L'essentiel de mon travail portait sur du développement mobile (iOS, Android) via Xamarin.Forms puis .net MAUI.
  </p>

  <p>
    J'ai cependant participé au Back-End : évolution de la base de données, intégration d'API, tests unitaires et création de pages web
  </p>

  <p>
    <em>.Net C#, Xamarin.Forms, XAML, LINQ, SQL, WCF, Microsoft AppCenter, Xamarin.UITest, tests unitaires, OneSignal, ASP .Net, Entity Framework, MVVM</em>
  </p>

    <h3 id="developpeur-independant-micro-entreprise">
      Développeur Indépendant (micro-entreprise)
    </h3>

  <p>
    <b><i>Janvier 2016 - avril 2021: télétravail 100%</i></b>
  </p>

  <p>
    Principaux clients :<br>
  </p>

    <h4 id="estay">
      ESTAY
    </h4>

  <p>
    Développement d'une application mobile de networking (voyage d'affaire)
<br>- Développement Xamarin.Forms (iOS et Android).
<br>- Mise-en-place de tests IHM avec Xamarin.UITest dans le projet Xamarin.Forms.<br>- Refactorisation du code, stabilisation du projet, éradication des régressions.<br>
  </p>

  <p>
    <em>.Net C#, Xamarin.Forms, XAML, LINQ, SQL, WCF, Microsoft AppCenter, Xamarin.UITest, tests unitaires,&nbsp;OneSignal,&nbsp;MVVM</em>
  </p>

    <h4 id="client-confidentiel">
      [Client confidentiel]
    </h4>

  <p>
    Travail de recherche et développement autour des méthodes de paiement à distance.
  </p>

  <p>
    <em>&nbsp;.Net C#,&nbsp;Xamarin.Forms (iOS et Android),&nbsp;UWP,&nbsp;Bluetooth LE,&nbsp;Communication ultra-son (framework LISNR),&nbsp;Reconnaissance faciale (framework Rank One Face Recognition),&nbsp;Geofencing (détection d'entrée/sortie de zone géographique), LINQ, XAML, MVVM</em>
  </p>

    <h4 id="tako-triperz">
      TAKO (Triperz)
    </h4>

  <p>
    Contribution au développement d'une application mobile Xamarin.Forms ayant l'ambition de devenir le UBER des Taxis.<br><br><em>XAML, LINQ, Xamarin.Forms, Visual Studio, C#, .NET, Realm, MVVM, API REST</em><br>
  </p>

    <h4 id="egis-ams">
      EGIS AMS
    </h4>

  <p>
    Interventions en support sur les projets de développement de logiciels métiers pour concessions d'autoroutes.<br><br><em>Visual Basic, .Net, C#, Winforms, WPF, XAML, LINQ, Visual Studio, SQL, Merise, Access, Oracle, géomatique, MapInfo, MapX, MapBasic, MapWinGis, ADO .Net, MVVM</em><br>
  </p>

    <h4 id="adic-informatique">
      Adic Informatique
    </h4>

  <p>
    Reprise à mon compte du contrat de maintenance abandonné par EGIS AMS. Maintenance et évolution d'une suite logicielle dédiée aux collectivités locales (cadastre, urbanisme, cimetière, voirie)<br><br><em>Visual Basic, Visual Studio, SQL, Merise, Access, géomatique, MapWinGis, ADODB</em><br>
  </p>

    <h4 id="asp-maisons-laffitte">
      ASP Maisons-Laffitte
    </h4>

  <p>
    Reprise à mon compte du contrat de maintenance abandonné par EGIS AMS<br>Maintenance et évolution du logiciel de gestion des adhérents du parc de Maisons-Laffitte.
  </p>

  <p>
    <em>Visual Basic, .Net, C#, LINQ, Visual Studio, SQL, Merise, Access, géomatique, MapWinGis, Winforms, ADODB</em>
  </p>

    <h3 id="ingenieur-developpement-cdi">
      Ingénieur développement (CDI)
    </h3>

  <p>
    <b><i>Octobre 2014 - décembre 2015 : EGIS AMS (Chenove 21)</i></b><br><br>Cette activité est la continuité de la précédente, la start-up Emash ayant été absorbée par le groupe EGIS pour devenir Egis Asset Management Solutions.
<br>
<br>Néanmoins, la plus grosse partie des efforts s'est portée durant cette période sur les logiciels de gestion de patrimoine autoroutier délaissant de fait les applications destinées aux collectivité locales.<br><br><em>Visual Basic, .Net, C#, Winforms,&nbsp;</em>WPF, XAML,&nbsp;<em>LINQ, Visual Studio, SQL, Merise, Access, Oracle, géomatique, MapInfo, MapX, MapBasic, MapWinGis, ADODB, MVVM</em><br>
  </p>

    <h3 id="developpeur-vb6-geomatique-net-c-formateur-hotliner-cdi">
      Développeur VB6, géomatique, .Net C#, formateur, hotliner (CDI)
    </h3>

  <p>
    <b><i>Aout 2007 - octobre 2014 : EMASH (Chenove 21)</i></b>
  </p>

  <p>
    Emash était une petite entreprise où tout était à faire et j'ai été son premier employé. J'y ai donc porté de multiples casquettes, et malgré un intitulé de "développeur" mes missions étaient plus variées que cela :
  </p>

  <p>
    - Développement d'applications métiers : .Net C# (WPF et WinForms), VB6, Géomatique (MapInfo, MapWinGis), Access, Oracle<br>- Support technique
<br>- Formation utilisateur
<br>- Encadrement de stagiaires et étudiants en alternance
<br>- Rédaction de documents : guide utilisateur, documentation, cahier de recettes…
<br>
<br>L'activité d'Emash concernait principalement deux secteurs d'activité :
<br>- Logiciels de gestion du patrimoine pour concessions d'autoroutes
<br>- Logiciels destinés aux collectivités locales (cadastre, urbanisme…)
<br>
<br>J'ai notamment développé, maintenu et fait évoluer une suite logicielle destinée aux collectivités locales. Installée chez plus de 2000 clients, puis rachetée en 2014 par un éditeur spécialisé (ADIC Informatique) pour lequel j'ai ensuite assuré la maintenance corrective et évolutive.
  </p>

  <p>
    J'ai développé en intégralité le logiciel de gestion des adhérents du parc de Maisons-Laffitte. Depuis l'expression du besoin client jusqu'au déploiement, en passant bien sûr par la modélisation des bases de données, la migration des données existantes et le développement applicatif.
  </p>

  <p>
    J'ai également participé au développement des logiciels métiers dédiés aux concessions d'autoroutes (APRR, A63, EGIS Road, Autoroute du Maroc…) :
  </p>

  <ul>
    <li>Logiciel d'intégration des données de l'état de la chaussée : calcul du niveau de dégradation global de la chaussée à partir d'indicateurs variés et géolocalisés</li><li>Logiciel embarqué (tablettes renforcées) pour saisie sur site des indicateurs d'inspections sur les infrastructures (bâtiments, ouvrages d'art, ouvrages hydraulique, signalétique…)</li><li>Logiciel de navigation image par image sur la chaussée à partir de prises de vue géolocalisées. Le logiciel permettait, après une phase de calibrage, de prendre des mesures directement sur les photographies</li>
  </ul>

  <p>
    <em>Visual Basic, .Net, C#, Winforms, WPF, XAML, LINQ, Visual Studio, SQL, Merise, Access, Oracle, MapInfo, MapX, MapBasic, MapWinGis, trames GPS, ADODB, Inno Setup, IS Tools</em>
  </p>

    <h3 id="programmeur-vb6-stage-bts">
      Programmeur VB6 (Stage BTS)
    </h3>

  <p>
    2006 : <a href="https://www.atolcd.com/" target="_blank" class="extlink extlink-icon-1"  >Atol C&amp;D</a> (Longvic 21)
  </p>

  <p>
    Migration de la partie SIG (cartographie) d'un outil de consultation de plans cadastraux de MapInfo vers le composant libre MapWinGis.
  </p>

    <h2 id="formationlessbrgreater">
      Formation<br>
    </h2>

    <h3 id="facture-instrumentale-lutherie-en-guitare">
      Facture instrumentale, lutherie en guitare
    </h3>

  <p>
    <em><strong>Novembre 2023 - aout 2024 (L'Esprit du bois, Cluny)</strong></em>
  </p>

  <p>
    Une pause bienvenue après 15 années cloué derrière un écran. J'y ai appris à utiliser mes mains en plus de mon cerveau, mais bien plus encore, <i>j'ai découvert un univers où contrairement à l'informatique, il n'est pas possible d'annuler une erreur</i>. Avec le bois, quand on se plante, on se plante. Et ça, c'est rudement formateur.
  </p>

    <h4 id="bts-informatique-de-gestion-option-developpeur-dapplications">
      BTS Informatique de Gestion, option développeur d'applications
    </h4>

  <p>
    <b><i>2004 - 2006 (CNED, préparation autonome de l'examen)</i></b><br>
  </p>

    <h4 id="dea-optique-mecanique-et-microsystemes-master">
      DEA Optique, mécanique et microsystèmes (Master)
    </h4>

  <p>
    <b><i>2003 (UFR Sciences et Techniques de Besançon, Laboratoire d'Optique P.M. Duffieux)</i></b>
  </p>

  <p>
    Cursus scientifique universitaire BAC+5.
  </p>

    <h2 id="langues">
      Langues
    </h2>

  <p>
    Français (langue maternelle)
  </p>

  <p>
    Anglais : je comprends les documentations technique, j'ai lu Harry Potter (les 7 tomes) en version originale, je comprends la plupart des vidéos anglophones que je regarde pour mes loisirs (principalement la lutherie). Par contre je parle anglais comme un français.
  </p>

    <h2 id="divers">
      Divers
    </h2>

  <ul>
    <li>Centres d'intérêt : lutherie, sciences et techniques, arts-martiaux (instructeur bénévole depuis 2016)</li><li>Création et gestion d'une association sportive (secrétaire général, trésorier et instructeur)</li><li>Gestion de microentreprise (5 ans en développeur freelance, artisanat depuis novembre 2024)</li><li>Permis B</li>
  </ul>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Je n&#x27;aime pas coder ! 😱</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/je-naime-pas-coder/"/>
        <id>https://www.sylvainmoingeon.fr/je-naime-pas-coder/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/46/frustration.jpeg" medium="image" />
            <category term="bonnes pratiques"/>

        <updated>2024-11-27T11:03:35+01:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/46/frustration.jpeg" alt="Un développeur mécontent devant son écran" />
                    Je suis développeur et je n'aime pas coder. Entendons nous bien. Je&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/46/frustration.jpeg" class="type:primaryImage" alt="Un développeur mécontent devant son écran" /></p>
                
  <p>
    Je suis développeur et je n'aime pas coder.
  </p>

  <p>
    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.
  </p>

  <p>
    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.
  </p>

    <figure class="post__image post__image--center">
      <a href="https://en.wikipedia.org/wiki/Philips_Videopac+_G7400" target="_blank">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/46/Philips_Videopac_G7400.jpg" height="414" width="550" alt="Photo d'une console Videopac+"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/46/responsive/Philips_Videopac_G7400-xs.jpg 300w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/Philips_Videopac_G7400-sm.jpg 480w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/Philips_Videopac_G7400-md.jpg 768w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/Philips_Videopac_G7400-lg.jpg 1024w">
      </a>
      <figcaption>Mon premier contact avec l'informatique ! (credits Marco van den Hout, license CC BY-SA 3.0)</figcaption>
    </figure>

  <p>
    
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.
  </p>

  <p>
    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.
<br>
<br>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.
<br>
<br>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.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/46/spaghetti.jpg" height="465" width="620" alt="Un câblage électronique inextricable"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/46/responsive/spaghetti-xs.jpg 300w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/spaghetti-sm.jpg 480w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/spaghetti-md.jpg 768w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/spaghetti-lg.jpg 1024w">
      <figcaption>Un jour, on m'a demandé de déboguer un code qui ressemblait à ça</figcaption>
    </figure>

  <p>
    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.
  </p>

  <p>
    Mais mon métier n'est pas codeur.
  </p>

  <p>
    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 !<br>
<br>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<a href="https://www.lutheriemoingeon.fr/" target="_blank" class="extlink extlink-icon-1"  > je façonne des guitares</a>.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/46/2474565999_df7624fddf_o.jpg" height="304" width="642" alt="Interface du logiciel DOS Voyetra Sequencer"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/46/responsive/2474565999_df7624fddf_o-xs.jpg 300w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/2474565999_df7624fddf_o-sm.jpg 480w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/2474565999_df7624fddf_o-md.jpg 768w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/2474565999_df7624fddf_o-lg.jpg 1024w">
      <figcaption>J'ai composé mes premières musiques sur ce type d'interface. Ce n'est clairement pas l'outil qui m'a attiré !</figcaption>
    </figure>

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

  <p>
    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 <a href="https://www.lutheriemoingeon.fr/" target="_blank" class="extlink extlink-icon-1"  >façonner le manche d'une guitare</a>. Je ne suis pas passionné de râpe et de rabot. Mais j'aime <a href="https://www.lutheriemoingeon.fr/" target="_blank" class="extlink extlink-icon-1"  >construire un instrument de musique</a> et m'émerveiller quand un musicien le joue une fois terminé.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/46/the-pragmatic-programmer.jpg" height="2545" width="2000" alt="Couverture du livre The Pragmatic Programmer"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/46/responsive/the-pragmatic-programmer-xs.jpg 300w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/the-pragmatic-programmer-sm.jpg 480w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/the-pragmatic-programmer-md.jpg 768w ,https://www.sylvainmoingeon.fr/media/posts/46/responsive/the-pragmatic-programmer-lg.jpg 1024w">
      <figcaption>Mais que vois-je ? Un rabot pour illustrer un livre d'informatique !</figcaption>
    </figure>

  <p>
    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.
  </p>

  <p>
    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 ?<br>
  </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Luthier en guitare en cours de déploiement sur Dijon</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/luthier-en-guitare-sur-dijon-reparation-reglage-entretien/"/>
        <id>https://www.sylvainmoingeon.fr/luthier-en-guitare-sur-dijon-reparation-reglage-entretien/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/45/20240402_154406.jpg" medium="image" />
            <category term="luthier dijon"/>

        <updated>2024-11-14T15:32:35+01:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/45/20240402_154406.jpg" alt="" />
                    Vous pouvez désormais me contacter pour toute prestation de réglage, entretien ou&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/45/20240402_154406.jpg" class="type:primaryImage" alt="" /></p>
                
  <p>
    Vous pouvez désormais me contacter pour toute prestation de <a href="https://www.lutheriemoingeon.fr/prestations-tarifs-luthier-en-guitare-dijon.html" target="_blank"  class=" extlink extlink-icon-1"  >réglage, entretien ou réparation de guitare</a>, qu'il s'agisse de guitare électrique, guitare classique ou guitare folk. Et j'ajoute aussi acoustique pour le référencement sur les moteurs de recherche. 🙂
  </p>

  <p>
    L'<a href="https://www.lutheriemoingeon.fr/atelier-lutherie-guitare-dijon.html"  target="_blank" class=" extlink extlink-icon-1"  >atelier de lutherie en guitare se situe à Chevigny-Saint-Sauveur</a>, près de Dijon et j'y reçois sur rendez-vous.
  </p>

  <p>
    <a href="https://www.lutheriemoingeon.fr/stage-reglage-entretien-guitare.html" target="_blank" class="extlink extlink-icon-1"  >Des stages de réglages et optimisation de guitare</a> sont également prévus, restez à&nbsp; l'écoute !
  </p>

  <p>
    Au plaisir de vous y voir !
  </p>

  <div  class="gallery-wrapper gallery-wrapper--wide">
    <div class="gallery" data-columns="3">
      <figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240313_170503.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240313_170503-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240320_143555.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240320_143555-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240606_143916.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240606_143916-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240607_152241.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240607_152241-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240702_160436.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240702_160436-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240719_165934.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240719_165934-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240831_173238.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/20240831_173238-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/45/gallery/defonces.jpg" data-size="3468x4624">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/45/gallery/defonces-thumbnail.jpg" height="1024" width="768" alt="" >
      </a>
      
    </figure>
    </div>
  </div>

  <p>
    
  </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>De développeur à luthier, histoire d&#x27;une reconversion</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/de-developpeur-a-luthier-histoire-dune-reconversion/"/>
        <id>https://www.sylvainmoingeon.fr/de-developpeur-a-luthier-histoire-dune-reconversion/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/43/cover.webp" medium="image" />
            <category term="reconversion"/>
            <category term="luthier"/>
            <category term="lutherie"/>
            <category term="guitare"/>
            <category term="facture instrumentale"/>
            <category term="développeur informatique"/>

        <updated>2024-08-20T19:43:05+02:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/43/cover.webp" alt="Illustration d&#x27;un article de blog sur la reconversion d&#x27;un développeur informatique au métier de la lutherie" />
                    Voilà un peu plus d'une année que je n'ai écrit une ligne&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/43/cover.webp" class="type:primaryImage" alt="Illustration d&#x27;un article de blog sur la reconversion d&#x27;un développeur informatique au métier de la lutherie" /></p>
                
  <p>
    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.
  </p>

  <p>
    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…
<br>
<br>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.
<br>
<br>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. 
<br>
<br>Et ce que je découvre n'est pas beau ! Ça craint !
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/burnout.webp" height="1024" width="1024" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/43/responsive/burnout-xs.webp 300w ,https://www.sylvainmoingeon.fr/media/posts/43/responsive/burnout-sm.webp 480w ,https://www.sylvainmoingeon.fr/media/posts/43/responsive/burnout-md.webp 768w ,https://www.sylvainmoingeon.fr/media/posts/43/responsive/burnout-lg.webp 1024w">
      
    </figure>

  <p>
    
<br>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 :
<br>
<br>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.
<br>
<br>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. 
<br>
<br>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.
<br>
<br>En journée je me rends à mes séances d'orthoptie parce qu'à force de fixer un écran&nbsp; 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.
<br>
<br>Je me rends subitement compte que mes deux bébés sont des ados. Je ne les ai pas vu grandir.
<br>
<br>Et en plus, j'ai 45 ans, l'heure de la crise !
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/frustration.jpeg" height="1024" width="1024" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/43/responsive/frustration-xs.jpeg 300w ,https://www.sylvainmoingeon.fr/media/posts/43/responsive/frustration-sm.jpeg 480w ,https://www.sylvainmoingeon.fr/media/posts/43/responsive/frustration-md.jpeg 768w ,https://www.sylvainmoingeon.fr/media/posts/43/responsive/frustration-lg.jpeg 1024w">
      
    </figure>

  <p>
    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.
<br>
<br>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.
<br>
<br>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 ?
<br>
<br>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 :
  </p>

  <ul>
    <li>Faire de la photo, une de mes passions laissée à l'abandon faute de temps</li><li>Capitaliser sur mes études scientifiques (BAC+5) pour revenir dans ce domaine<br></li><li>Me professionnaliser au niveau du sport (je suis animateur bénévole dans un club d'arts-martiaux)<br></li><li>Trouver une activité en lien avec la musique (passionné de guitare)<br></li><li>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 ?")<br></li>
  </ul>

  <p>
    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.
<br>
<br>Et peu-à-peu, celui-ci se dessine, la balance penche de plus en plus vers la lutherie.
<br>
<br>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. :-)
  </p>

  <div  class="gallery-wrapper gallery-wrapper--wide">
    <div class="gallery" data-columns="3">
      <figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20231128_184139.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20231128_184139-thumbnail.jpg" height="576" width="768" alt="Des instruments de musique pendus au plafond dans un atelier de lutherie" >
      </a>
      <figcaption>Ambiance au centre de formation</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20231129_112248.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20231129_112248-thumbnail.jpg" height="576" width="768" alt="Photo illustrant des moules utilisés pour fabriquer des guitares" >
      </a>
      <figcaption>Une belle collection de moules</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240301_095101.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240301_095101-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le cintrage d'une éclisse" >
      </a>
      <figcaption>Un pan coupé qui m'a donné du fil (ou plutôt du bois) à retordre</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240301_160706.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240301_160706-thumbnail.jpg" height="576" width="768" alt="" >
      </a>
      
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240402_154406.jpg" data-size="2576x1932">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240402_154406-thumbnail.jpg" height="576" width="768" alt="Photo illustrant un ciseau à bois affleurant un filet dans une rosace de guitare" >
      </a>
      <figcaption>Affleurage d'un filet de rosace</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240404_165316.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240404_165316-thumbnail.jpg" height="576" width="768" alt="Photo illustrant un collage au ciel" >
      </a>
      <figcaption>Collage au ciel</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240405_162705.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240405_162705-thumbnail.jpg" height="576" width="768" alt="Photo illustrant les barrages de table d'harmonie dans une guitare" >
      </a>
      <figcaption>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</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240411_165205.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240411_165205-thumbnail.jpg" height="576" width="768" alt="Photo illustrant une vielle à roue de 1742 et sa réplique exacte" >
      </a>
      <figcaption>Une très vieille vielle (1742) et sa réplique en vue de conservation</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240513_175718.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240513_175718-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le collage des filets d'éclisse sur une guitare classique" >
      </a>
      <figcaption>Collage des filets avec la technique ultra moderne et sophistiquée des "bouts de scotch"</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240516_121424.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240516_121424-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le chanfrein de confort d'une guitare classique" >
      </a>
      <figcaption>Chanfrein de confort</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240607_152241.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240607_152241-thumbnail.jpg" height="576" width="768" alt="Photo illustrant un luthier travaillant au racloir" >
      </a>
      <figcaption>On s'occupe du sillon interfessier de la guitare (vocabulaire très officiel, il va sans dire)</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240613_120653.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240613_120653-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le profilage d'un manche de guitare" >
      </a>
      <figcaption>Profilage du manche</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240613_161832.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240613_161832-thumbnail.jpg" height="576" width="768" alt="Photo illustrant l'emboitement du manche sur la caisse" >
      </a>
      <figcaption>Emboitement du manche sur la caisse</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_164653.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_164653-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le talon de la guitare" >
      </a>
      <figcaption>Décoration du talon en érable pour la continuité avec les filets</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_164717.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_164717-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le talon de la guitare" >
      </a>
      <figcaption>Décoration du talon en érable pour la continuité avec les filets</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_175523.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240621_175523-thumbnail.jpg" height="576" width="768" alt="Photo illustrant une guitare assemblée mais pas encore terminée" >
      </a>
      <figcaption>Ça semble presque terminé, mais comme en informatique, quand c'est presque fini, ça ne fait que commencer !</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240627_111132.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240627_111132-thumbnail.jpg" height="576" width="768" alt="Photo illustrant l'usinage d'un chevalet de guitare" >
      </a>
      <figcaption>Usinage du chevalet</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240702_162427.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240702_162427-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le chevalet terminé" >
      </a>
      <figcaption>Le chevalet prêt à être posé</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240705_110756.jpg" data-size="4624x3468">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/43/gallery/20240705_110756-thumbnail.jpg" height="576" width="768" alt="Photo illustrant le collage du chevalet" >
      </a>
      <figcaption>Collage du chevalet (la guitare a été vernie entre temps)</figcaption>
    </figure>
    </div>
  </div>

  <p>
    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 ?
<br>
<br>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).
<br>
<br>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 !<br>
<br>Aurai-je découvert mon Ikigaï ?
<br>
<br>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.
  </p>

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

  <p>
    <a href="https://www.lutheriemoingeon.fr/" target="_blank" class="extlink extlink-icon-1"  >Mon atelier de lutherie sur Dijon (Chevigny-Saint-Sauveur)</a>&nbsp;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).
  </p>

  <p>
    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. 😉
  </p>

  <p>
    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 !
  </p>

  <p>
    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 !
  </p>

  <p>
    Bien merci à toi qui a tout lu jusqu'ici !
<br>
<br>Ah, et oui, zut : like, partage et tout ça, hein.
  </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Authentification Azure Directory B2C dans une application Xamarin.Forms</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/authentification-azure-directory-b2c-dans-une-application-xamarinforms/"/>
        <id>https://www.sylvainmoingeon.fr/authentification-azure-directory-b2c-dans-une-application-xamarinforms/</id>
            <category term="Xamarin.Forms"/>
            <category term="Azure"/>
            <category term="Active Directory B2C"/>

        <updated>2021-03-19T18:21:07+01:00</updated>
            <summary type="html">
                <![CDATA[
                    <p>On m’a récemment demandé d’étudier la faisabilité d’une authentification depuis Azure Directory B2C dans une application Xamarin.Forms.</p><p>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…).</p><p>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 <em>Directory B2C</em> sur Azure jusqu’à l’authentification et la récupération des données de l’utilisateur dans l’application Xamarin.Forms.</p>
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p>On m’a récemment demandé d’étudier la faisabilité d’une authentification depuis Azure Directory B2C dans une application Xamarin.Forms.</p><p>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…).</p><p>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 <em>Directory B2C</em> sur Azure jusqu’à l’authentification et la récupération des données de l’utilisateur dans l’application Xamarin.Forms.</p>

<h1 id="résumé">Résumé</h1>
<ol>
<li>Créer un tenant <em>Active Directory B2C</em> dans Azure.</li>
<li>Enregistrer l’application mobile dans le tenant <em>Active Directory B2C</em>.</li>
<li>Créer des <em>User Flows</em> avec des polices de <em>Signin</em>, réinitialisation de mot de passe…</li>
<li>Utiliser la <em>Microsoft Authentication Library (MSAL)</em> pour gérer le flux d’authentification dans l’application.</li>
</ol>
<h2 id="sources">Sources</h2>
<ul>
<li><a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/authentication/azure-ad-b2c"  class="extlink extlink-icon-1"  >Authenticate Users with Azure Active Directory B2C</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant"  class="extlink extlink-icon-1"  >Tutorial: Create an Azure Active Directory B2C tenant</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga"  class="extlink extlink-icon-1"  >Tutorial: Register a web application in Azure Active Directory B2C</a></li>
</ul>
<h1 id="côté-serveur">Côté serveur</h1>
<h2 id="création-du-compte-azure">Création du compte Azure</h2>
<p><a href="https://aka.ms/azfree-docs-mobileapps"  class="extlink extlink-icon-1"  >https://aka.ms/azfree-docs-mobileapps</a></p><p>Pré-requis :</p><ul>
<li>Un compte microsoft</li>
<li>Une carte bancaire</li>
</ul>
<p>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.</p><h2 id="configuration-du-portail-en-anglais">Configuration du portail en ANGLAIS</h2>
<p>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 <em>l’emplacement du groupe de ressource</em> est invalide alors qu’on la sélectionne dans une liste (et peu importe la valeur sélectionnée) !</p><p>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 !</p><p>Donc évitons-nous bien des soucis et passons immédiatement le portail en anglais.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/6ce4a4c4b1004751bddf92e7f88a9f98.png" alt="Configuration du portail en anglais" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/6ce4a4c4b1004751bddf92e7f88a9f98-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/6ce4a4c4b1004751bddf92e7f88a9f98-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/6ce4a4c4b1004751bddf92e7f88a9f98-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/6ce4a4c4b1004751bddf92e7f88a9f98-lg.png 1024w"></figure></p><h2 id="enregistrement-du-namespace-mirosoftazureactivedirectory">Enregistrement du namespace <em>Mirosoft.AzureActiveDirectory</em></h2>
<p>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. :-)</p><figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/eae4e6e303ba45cfaf377ae3ab2f522e.png" alt="Message d&#39;erreur concernant Microsoft.AzureActiveDirectory" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/eae4e6e303ba45cfaf377ae3ab2f522e-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/eae4e6e303ba45cfaf377ae3ab2f522e-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/eae4e6e303ba45cfaf377ae3ab2f522e-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/eae4e6e303ba45cfaf377ae3ab2f522e-lg.png 1024w"></figure><p><strong>Cliquez sur <em>Subscriptions</em></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/d821b9e2f1a84d46b574a439df9c1fc9.png" alt="Navigation vers les subscriptions" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/d821b9e2f1a84d46b574a439df9c1fc9-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/d821b9e2f1a84d46b574a439df9c1fc9-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/d821b9e2f1a84d46b574a439df9c1fc9-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/d821b9e2f1a84d46b574a439df9c1fc9-lg.png 1024w"></figure></p><p><strong>Dans la liste, sélectionnez votre subscription courante.</strong></p><p>Si vous venez de créer un compte gratuit, il n’y en aura qu’une :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/13427f5008fb459db2ea5276dd729262.png" alt="Sélection de la subscription" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/13427f5008fb459db2ea5276dd729262-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/13427f5008fb459db2ea5276dd729262-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/13427f5008fb459db2ea5276dd729262-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/13427f5008fb459db2ea5276dd729262-lg.png 1024w"></figure></p><p><strong>Dans le panneau de gauche, cliquez sur <em>Resource providers</em></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/979a2d1abb4a48b59a7bf9ad8486880f.png" alt="Navigation vers les resource providers" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/979a2d1abb4a48b59a7bf9ad8486880f-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/979a2d1abb4a48b59a7bf9ad8486880f-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/979a2d1abb4a48b59a7bf9ad8486880f-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/979a2d1abb4a48b59a7bf9ad8486880f-lg.png 1024w"></figure></p><p><strong>Cherchez Azure, sélectionnez <em>Microsoft.AzureActiveDirectory</em> et <strong>cliquez sur Register</strong></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/8fa48e6076574a9eb090837d9231fd87.png" alt="Recherche de Microsoft.AzureActiveDirectory" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fa48e6076574a9eb090837d9231fd87-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fa48e6076574a9eb090837d9231fd87-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fa48e6076574a9eb090837d9231fd87-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fa48e6076574a9eb090837d9231fd87-lg.png 1024w"></figure></p><p>Patientez… patientez… patientez… patientez…
Cliquez de temps en temps sur <em>Refresh</em> au cas où.
Si ça ne fonctionne pas, recommencez (sélection, register), au bout d’un moment ça finit par passer. :-)
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/941ed15f975649aca20eeb624c4192fd.png" alt="Microsoft.AzureActiveDirectory est enregistré" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/941ed15f975649aca20eeb624c4192fd-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/941ed15f975649aca20eeb624c4192fd-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/941ed15f975649aca20eeb624c4192fd-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/941ed15f975649aca20eeb624c4192fd-lg.png 1024w"></figure></p><p>C’est bon, on y est, on peut commencer !</p><h2 id="création-de-lactive-directory-b2c">Création de l’Active Directory B2C</h2>
<p><strong>Cliquez sur <em>Create a resource</em></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/26cd2d69e0cf4e2c9533a45c7ef1e726.png" alt="Créer une ressources" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/26cd2d69e0cf4e2c9533a45c7ef1e726-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/26cd2d69e0cf4e2c9533a45c7ef1e726-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/26cd2d69e0cf4e2c9533a45c7ef1e726-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/26cd2d69e0cf4e2c9533a45c7ef1e726-lg.png 1024w"></figure></p><p><strong>Cherchez et sélectionnez <em>Active Directory B2C</em></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/3734c6c0c609497b8523c2ac5ee56045.png" alt="Sélectionner Active Directory B2C" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/3734c6c0c609497b8523c2ac5ee56045-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/3734c6c0c609497b8523c2ac5ee56045-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/3734c6c0c609497b8523c2ac5ee56045-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/3734c6c0c609497b8523c2ac5ee56045-lg.png 1024w"></figure></p><p><strong>Cliquez sur <em>Create</em></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/8fb8757ce94343aea6ddb6b375e139ac.png" alt="Cliquer sur Create" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fb8757ce94343aea6ddb6b375e139ac-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fb8757ce94343aea6ddb6b375e139ac-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fb8757ce94343aea6ddb6b375e139ac-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/8fb8757ce94343aea6ddb6b375e139ac-lg.png 1024w"></figure></p><p><strong>Create a New Azure AD B2C tenant</strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/0f44978924974827b83e5498624e1827.png" alt="Create a New Azure AD B2C tenant" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/0f44978924974827b83e5498624e1827-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/0f44978924974827b83e5498624e1827-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/0f44978924974827b83e5498624e1827-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/0f44978924974827b83e5498624e1827-lg.png 1024w"></figure></p><p><strong>Renseignez les informations du tenant</strong></p><p>Créer un <em>Groupe de ressources</em> avec le lien <em>Create New</em> s’il n’en existe pas déjà un.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/bfa01cbf52b14c7b9969c162f08e83ba.png" alt="Renseigner les informations du tenant" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/bfa01cbf52b14c7b9969c162f08e83ba-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bfa01cbf52b14c7b9969c162f08e83ba-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bfa01cbf52b14c7b9969c162f08e83ba-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bfa01cbf52b14c7b9969c162f08e83ba-lg.png 1024w"></figure></p><p><strong>Cliquez sur <em>Review + Create</em> pour valider les informations</strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/eeac2be3682d457dae69874ee0e04525.png" alt="Valider les informations" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/eeac2be3682d457dae69874ee0e04525-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/eeac2be3682d457dae69874ee0e04525-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/eeac2be3682d457dae69874ee0e04525-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/eeac2be3682d457dae69874ee0e04525-lg.png 1024w"></figure></p><p><strong>Cliquez sur <em>Create</em> pour en finir !</strong></p><p>Patientez. Tant que ça gigote là-dedans c’est que ça bosse :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/2fdbea7151f948278ae450c27839495d.png" alt="Indicateur d&#39;activité dans la toolbar" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/2fdbea7151f948278ae450c27839495d-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/2fdbea7151f948278ae450c27839495d-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/2fdbea7151f948278ae450c27839495d-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/2fdbea7151f948278ae450c27839495d-lg.png 1024w"></figure></p><p>Cliquez sur le lien, bienvenue à Black Mesa !
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/000cf65ea90e48b0b774d8405119b298.png" alt="Lien menant vers votre tenant Active Directory B2C" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/000cf65ea90e48b0b774d8405119b298-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/000cf65ea90e48b0b774d8405119b298-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/000cf65ea90e48b0b774d8405119b298-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/000cf65ea90e48b0b774d8405119b298-lg.png 1024w"></figure></p><h2 id="configuration-dune-application-côté-serveur">Configuration d’une <em>application</em> côté serveur</h2>
<h3 id="selection-du-tenant-b2c">Selection du tenant B2C</h3>
<p>Dans la toolbar en haut à droite, trouvez le bouton qui ressemble à un carnet avec un entonnoir :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/0dc10f53aef94e8cb578f3eb0351b5bc.png" alt="Buton de sélection des directories" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/0dc10f53aef94e8cb578f3eb0351b5bc-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/0dc10f53aef94e8cb578f3eb0351b5bc-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/0dc10f53aef94e8cb578f3eb0351b5bc-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/0dc10f53aef94e8cb578f3eb0351b5bc-lg.png 1024w"></figure></p><p>Mais en principe vous devriez déjà être dessus si vous avez cliqué sur le lien à la fin de l’étape précédente.</p><p>Si vous vous retrouvez sur une page d’accueil comme ça et que vous ne retrouvez pas votre <em>tenant AD B2C</em>…
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/619e486c4a054f2cb3ee928a143a1963.png" alt="Page d&#39;accueil du Directory" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/619e486c4a054f2cb3ee928a143a1963-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/619e486c4a054f2cb3ee928a143a1963-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/619e486c4a054f2cb3ee928a143a1963-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/619e486c4a054f2cb3ee928a143a1963-lg.png 1024w"></figure></p><p>Saisissez simplement <em>B2C</em> dans la barre de recherche en haut et dans la rubrique <em>Services</em>, sélectionnez <em>Azure AD B2C</em>.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/636efe75e666412e9c7584f700c45f51.png" alt="Recherche du tenant B2C" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/636efe75e666412e9c7584f700c45f51-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/636efe75e666412e9c7584f700c45f51-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/636efe75e666412e9c7584f700c45f51-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/636efe75e666412e9c7584f700c45f51-lg.png 1024w"></figure></p><h3 id="enregistrement-de-lapplication">Enregistrement de l’application</h3>
<p><strong>Sélectionnez <em>App registrations</em>…</strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/1c9cbee5394f46a397a8efbdcf1dc132.png" alt="Navigation vers l&#39;enregistrement d&#39;applications" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/1c9cbee5394f46a397a8efbdcf1dc132-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/1c9cbee5394f46a397a8efbdcf1dc132-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/1c9cbee5394f46a397a8efbdcf1dc132-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/1c9cbee5394f46a397a8efbdcf1dc132-lg.png 1024w"></figure></p><p><strong>Puis <em>New registration</em></strong>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/5f5448c2423e48b4bdbc92b536d2453d.png" alt="Nouvel enregistrement" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/5f5448c2423e48b4bdbc92b536d2453d-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/5f5448c2423e48b4bdbc92b536d2453d-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/5f5448c2423e48b4bdbc92b536d2453d-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/5f5448c2423e48b4bdbc92b536d2453d-lg.png 1024w"></figure></p><p><strong>Renseignez les informations</strong></p><ul>
<li><p><em>Nom</em> et <em>type de compte</em> supporté :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/ebba5c97abd241ac81af607acb811a19.png" alt="Nom et type de compte" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/ebba5c97abd241ac81af607acb811a19-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ebba5c97abd241ac81af607acb811a19-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ebba5c97abd241ac81af607acb811a19-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ebba5c97abd241ac81af607acb811a19-lg.png 1024w"></figure></p></li>
<li><p><em>Redirect URI</em> :
  Cela dépend du type d’application (application mobile, web app…).
  Dans notre cas, il s’agira d’une application mobile Xamarin.Forms.
  <strong>Attention, l’uri répond à un schéma bien particulier :</strong>
  <code>msal[redirect_uri]://auth</code>.
  <figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/f551892b4b9f48a8973694ff7473b391.png" alt="Redirect URi" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/f551892b4b9f48a8973694ff7473b391-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f551892b4b9f48a8973694ff7473b391-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f551892b4b9f48a8973694ff7473b391-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f551892b4b9f48a8973694ff7473b391-lg.png 1024w"></figure></p></li>
</ul>
<p>Cet URi sera à renseigner dans les manifestes Android et iOS.</p><ul>
<li><em>Permissions :</em></li>
</ul>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/f6990694e50e4f59bbf304ab240ed75f.png" alt="Permisssions" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/f6990694e50e4f59bbf304ab240ed75f-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f6990694e50e4f59bbf304ab240ed75f-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f6990694e50e4f59bbf304ab240ed75f-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f6990694e50e4f59bbf304ab240ed75f-lg.png 1024w"></figure><p>Validez le tout en cliquant sur le bouton <em>Register</em>.
Bravo ! Votre application est enregistrée :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/21fa88ac0c1e4b8d97cba7c22496698f.png" alt="Application enregistrée" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/21fa88ac0c1e4b8d97cba7c22496698f-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/21fa88ac0c1e4b8d97cba7c22496698f-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/21fa88ac0c1e4b8d97cba7c22496698f-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/21fa88ac0c1e4b8d97cba7c22496698f-lg.png 1024w"></figure></p><h3 id="mais-ce-nest-pas-fini--la-case-à-cocher-secrète">Mais ce n’est pas fini : la case à cocher secrète</h3>
<p>Si vous vous en tenez là et suivez le tuto Microsoft et l’application <em>Sample</em> 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 !</p><p>Rendez-vous dans la rubrique <em>Authentication</em> et cochez les cases :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/cde6cd6048fa407e81ed0c4ba8559687.png" alt="Cocher les Uri d&#39;authentification" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/cde6cd6048fa407e81ed0c4ba8559687-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/cde6cd6048fa407e81ed0c4ba8559687-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/cde6cd6048fa407e81ed0c4ba8559687-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/cde6cd6048fa407e81ed0c4ba8559687-lg.png 1024w"></figure></p><p>Pensez bien à sauvegarder. Le bouton en haut, je vous laisse le trouver tout seul.</p><h3 id="création-des-user-flows">Création des <em>User Flows</em></h3>
<p>Créer un <em>user flow</em> revient à autoriser les utilisateurs à interagir avec leur compte AD : se connecter, réinitialiser son mot de passe, modifier son profile…</p><p>NB : les flows sont partageables entre les applications.</p><h4 id="sign-in">Sign In</h4>
<p>Au minima, les utilisateurs auront besoin de se connecter.
Dans la page de votre <em>tenant AD B2C</em>, allez sur <em>User flows</em> dans la rubrique <em>Policies</em> et cliquez sur <em>New user flow</em>.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/ff407a8e293349ff8cc82e83e5a3e4c7.png" alt="New User Flow" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff407a8e293349ff8cc82e83e5a3e4c7-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff407a8e293349ff8cc82e83e5a3e4c7-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff407a8e293349ff8cc82e83e5a3e4c7-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff407a8e293349ff8cc82e83e5a3e4c7-lg.png 1024w"></figure></p><p>Puis sélectionnez <em>Sign In</em> et créez le flow dans sa version recommandée.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/436019a0fe0643ebbcb99156b00edbbe.png" alt="Signin User Flow" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/436019a0fe0643ebbcb99156b00edbbe-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/436019a0fe0643ebbcb99156b00edbbe-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/436019a0fe0643ebbcb99156b00edbbe-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/436019a0fe0643ebbcb99156b00edbbe-lg.png 1024w"></figure></p><p>Renseignez les informations en fonction du mode de connexion et de l’authentification multiple souhaités.</p><p>Notez bien le nom, vous en aurez besoin côté client :
<figure class="post__image"><img decoding="auto" src="https://www.sylvainmoingeon.fr/media/posts/42/67382c4d40224b36a0f04c9054364cae.png" alt="Nom de la police" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/67382c4d40224b36a0f04c9054364cae-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/67382c4d40224b36a0f04c9054364cae-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/67382c4d40224b36a0f04c9054364cae-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/67382c4d40224b36a0f04c9054364cae-lg.png 1024w"></figure></p><p>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 <em>Show more…</em> pour obtenir la liste complète.</p><figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/f65a66c9662f40dc9aaf15676ed7bd5a.png" alt="Claims" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/f65a66c9662f40dc9aaf15676ed7bd5a-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f65a66c9662f40dc9aaf15676ed7bd5a-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f65a66c9662f40dc9aaf15676ed7bd5a-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/f65a66c9662f40dc9aaf15676ed7bd5a-lg.png 1024w"></figure><p>Finalisez en cliquant sur le bouton <em>Create</em>.</p><h4 id="autres-flows">Autres flows</h4>
<p>La procédure est similaire pour les autres types de <em>flows</em>. Créez ceux dont vous avez besoin.</p><h3 id="création-des-utilisateurs">Création des utilisateurs</h3>
<p>Sur la page de votre <em>tenant Azure AD B2C</em>, cliquez sur <em>Users</em> dans la rubrique <em>Manage</em>.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/ce85fccb56374fcab27e51b008093cde.png" alt="Création des utilisateurs" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/ce85fccb56374fcab27e51b008093cde-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ce85fccb56374fcab27e51b008093cde-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ce85fccb56374fcab27e51b008093cde-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ce85fccb56374fcab27e51b008093cde-lg.png 1024w"></figure></p><p>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 <em>Identity providers</em>.</p><p>A priori, tout est en place côté serveur, reste à implémenter la couche MSAL côté client.</p><h1 id="côté-client">Côté client</h1>
<h2 id="package-nuget-microsoft-authentication-library-msal">Package nuget <em>Microsoft Authentication Library (MSAL)</em></h2>
<p>Direction le nuget package manager.
Installez <em>Microsoft.Identity.Client</em> sur le projet commun et les projets des plateformes :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/bf2ed89bfb2644a79b5f1cd0c9857761.png" alt="Package nuget Microsoft.Identity.Client" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/bf2ed89bfb2644a79b5f1cd0c9857761-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bf2ed89bfb2644a79b5f1cd0c9857761-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bf2ed89bfb2644a79b5f1cd0c9857761-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bf2ed89bfb2644a79b5f1cd0c9857761-lg.png 1024w"></figure></p><h2 id="préparation-du-projet-commun">Préparation du projet commun</h2>
<h3 id="définition-de-quelques-constantes">Définition de quelques constantes</h3>
<p>Pour commencer, il est nécessaire de définir quelques constantes.</p><ul>
<li><p><code>tenantName</code> : le nom de domaine que vous avez défini pour votre tenant Directory B2C</p></li>
<li><p><code>tenantId</code> : le même suffixé par <code>.onmicrosoft.com</code></p></li>
<li><p><code>clientId</code> : <em>Application (client) Id</em>, vous trouverez cette information dans l’<em>overview</em> de votre application sur le portail Azure :
  <figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/99664b9fc9504d75b03489fffe708bfe.png" alt="ClientId dans l&#39;overview de l&#39;application" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/99664b9fc9504d75b03489fffe708bfe-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/99664b9fc9504d75b03489fffe708bfe-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/99664b9fc9504d75b03489fffe708bfe-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/99664b9fc9504d75b03489fffe708bfe-lg.png 1024w"></figure></p></li>
<li><p><code>redirect_uri</code> : l’uri enregistré côté azure</p></li>
<li><p><code>policySignin</code> et <code>policyPassword</code> : le nom des polices telles que vous les avez définies précédemment</p></li>
<li><p><code>iosKeychainSecurityGroup</code> : identifiant du Bundle iOS que vous trouvez dans <code>Info.plist</code>.</p></li>
</ul>
<pre><code class="language-csharp">public static class Constants
{
    const string tenantName = &quot;[your_tenant_name]&quot;;
    const string tenantId = &quot;[your_tenant_name].onmicrosoft.com&quot;;
    const string clientId = &quot;[your_application_client_id]&quot;;
    const string redirectUri = &quot;[redirect_uri]&quot;;
    const string policySignin = &quot;B2C_1_signin&quot;;
    const string policyPassword = &quot;B2C_1_resetpassword&quot;;
    const string iosKeychainSecurityGroup = &quot;[application iOS Bundle identifier]&quot;;
    
    static readonly string[] scopes = { &quot;&quot; };
    static readonly string authorityBase = $&quot;https://{tenantName}.b2clogin.com/tfp/{tenantId}/&quot;;

    public static string RedirectMsalScheme =&gt; $&quot;msal{redirectUri}://auth&quot;; 
    public static string AuthorityBase =&gt; authorityBase;
    public static string[] Scopes =&gt; scopes;
    public static string PolicySignin =&gt; policySignin;
    public static string PolicyPassword =&gt; policyPassword;
    public static string IosKeychainSecurityGroups =&gt; iosKeychainSecurityGroup;
    public static string ClientId =&gt; clientId;
    public static string AuthoritySignin =&gt; $&quot;{authorityBase}{policySignin}&quot;;
    public static string AuthorityPasswordReset =&gt; $&quot;{authorityBase}{policyPassword}&quot;;
}
</code></pre>
<h3 id="le-service-dauthentification--authenticationclient">Le service d’authentification : <code>AuthenticationClient</code></h3>
<p>Dans le fichier <code>App.xaml.cs</code>, définir les propriétés <code>AppUi</code> (nécessaire pour l’implémentation dans Android) et <code>AuthenticationClient</code> :</p><pre><code class="language-csharp">using Microsoft.Identity.Client;
[...]

public partial class App : Application
{
    public static IPublicClientApplication AuthenticationClient { get; private set; }
    public static object UIParent { get; set; } = null;
    
    [...]
}
</code></pre>
<p>Le service d’authentification implémente <code>IPublicClientApplication</code>, pour l’instancier, on utilise le builder <code>PublicClientApplicationBuilder</code> :</p><pre><code class="language-csharp">public App()
{
    InitializeComponent();

    AuthenticationClient = PublicClientApplicationBuilder.Create(Constants.ClientId)
            .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
            .WithB2CAuthority(Constants.AuthoritySignin)
            .WithRedirectUri(Constants.RedirectMsalScheme)
            .Build();
    [...]
}
</code></pre>
<h2 id="configuration-de-lapplication-ios">Configuration de l’application iOS</h2>
<h3 id="infoplist">Info.plist</h3>
<p>Souvenez-vous côté serveur, vous avez configuré un <em>Redirect Uri</em> sous la forme <code>msal[redirect_uri]://auth</code></p><p>Il s’agit ici de renseigner cette valeur dans le fichier Info.plist du projet iOS.
Cela se passe dans l’onglet <em>Advanced</em>, rubrique <em>URL Types</em> :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/e69e5ca4a86d40aaba291deee5c60caf.png" alt="Redirect Uri dans Info.plist" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/e69e5ca4a86d40aaba291deee5c60caf-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/e69e5ca4a86d40aaba291deee5c60caf-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/e69e5ca4a86d40aaba291deee5c60caf-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/e69e5ca4a86d40aaba291deee5c60caf-lg.png 1024w"></figure></p><h3 id="keychain-dans-entitlementsplist">Keychain dans Entitlements.plist</h3>
<p>Il est ensuite nécessaire d’enregistrer un Keychain dans <em>Entitlements.plist</em> :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/ed660e0f6cf54409b5237432c27a3909.png" alt="Keychain dans Entitlements.plist" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/ed660e0f6cf54409b5237432c27a3909-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ed660e0f6cf54409b5237432c27a3909-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ed660e0f6cf54409b5237432c27a3909-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ed660e0f6cf54409b5237432c27a3909-lg.png 1024w"></figure></p><p>Si cela ne s’est pas fait automatiquement, vérifiez bien que le fichier <em>Entitlements.plist</em> est sélectionné comme <em>Custom Entitlements</em> dans l’onglet <em>iOS Bundle Signing</em> du projet iOS.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/bd0f850cd2e8445688885dbc6c689b59.png" alt="Custom Entitlements" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/bd0f850cd2e8445688885dbc6c689b59-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bd0f850cd2e8445688885dbc6c689b59-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bd0f850cd2e8445688885dbc6c689b59-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bd0f850cd2e8445688885dbc6c689b59-lg.png 1024w"></figure></p><p>Faîtes-le bien pour chacune de vos configurations de Build, sinon vous aurez une exception à l’exécution concernant le Keychain !</p><h3 id="surcharge-de-la-méthode-openurl-dans-appdelegate">Surcharge de la méthode OpenUrl dans AppDelegate</h3>
<pre><code class="language-csharp">using Microsoft.Identity.Client;
[...]

[Register(&quot;AppDelegate&quot;)]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    [...]
    
    public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
    {
        AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
        return base.OpenUrl(app, url, options);
    }
}
</code></pre>
<h2 id="configuration-lapplication-android">Configuration l’application Android</h2>
<h3 id="manifeste">Manifeste</h3>
<p>Comme pour iOS, le <em>Redirect Uri</em> doit être déclaré dans le manifeste Android.</p><pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; android:versionCode=&quot;1&quot; android:versionName=&quot;1.0&quot; package=&quot;com.companyname.adb2capp&quot;&gt;
    &lt;uses-sdk android:minSdkVersion=&quot;21&quot; android:targetSdkVersion=&quot;30&quot; /&gt;
    &lt;application android:label=&quot;ADB2CApp.Android&quot; android:theme=&quot;@style/MainTheme&quot;&gt;
        
        &lt;!-- MSAL REGISTRATION START --&gt;
        &lt;activity android:name=&quot;microsoft.identity.client.BrowserTabActivity&quot;&gt;
            &lt;intent-filter&gt;
                &lt;action android:name=&quot;android.intent.action.VIEW&quot; /&gt;
                &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; /&gt;
                &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; /&gt;
                &lt;data android:scheme=&quot;msal[redirect_uri]&quot; android:host=&quot;auth&quot; /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
        &lt;!-- MSAL REGISTRATION END --&gt;
    &lt;/application&gt;
    &lt;uses-permission android:name=&quot;android.permission.ACCESS_NETWORK_STATE&quot; /&gt;
&lt;/manifest&gt;
</code></pre>
<h3 id="modifications-dans-mainactivity">Modifications dans <em>MainActivity</em></h3>
<p>L’authentification MSAL nécessite :</p><ul>
<li>De passer l’activity à <em>App.UIParent</em></li>
<li>De surcharger <em>OnActivityResult</em></li>
</ul>
<pre><code class="language-csharp">using Microsoft.Identity.Client;
[...]

public class MainActivity : FormsAppCompatActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(bundle);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App());
        // ADDED
        App.UIParent = this;
    }

    // OVERRIDED
    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);
        AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);
    }
    
    [...]
}
</code></pre>
<h2 id="implémentation-dans-le-projet-xamarinforms">Implémentation dans le projet Xamarin.Forms</h2>
<h3 id="création-dune-page-de-login">Création d’une page de Login</h3>
<p>On va faire super simple, créez une page <code>LoginPage.xaml</code>, ajoutez-y un bouton <code>Login</code> et abonnez-le à l’événement <code>Clicked</code>.</p><pre><code class="language-xml">&lt;ContentPage.Content&gt;
    &lt;Button x:Name=&quot;LoginButton&quot;
        Text=&quot;LOGIN&quot;
        Clicked=&quot;LoginButton_Clicked&quot;
        VerticalOptions=&quot;Center&quot;
        HorizontalOptions=&quot;Center&quot;
        /&gt;
&lt;/ContentPage.Content&gt;
</code></pre>
<p>Visuellement, difficile de faire plus basique :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/c2336499611b40d4a380610ef52dabe7.png" alt="Login Page" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/c2336499611b40d4a380610ef52dabe7-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/c2336499611b40d4a380610ef52dabe7-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/c2336499611b40d4a380610ef52dabe7-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/c2336499611b40d4a380610ef52dabe7-lg.png 1024w"></figure></p><p>A l’apparition de la page, appelez <code>AcquireTokenSilentAsync</code> pour rafraîchir le token d’authentification, au cas où un utilisateur soit déjà authentifié.</p><pre><code class="language-csharp">public partial class LoginPage : ContentPage
{
    [...]

    protected override async void OnAppearing()
    {
        try
        {
            // Look for existing account
            IEnumerable&lt;IAccount&gt; accounts = await App.AuthenticationClient.GetAccountsAsync();
            
            AuthenticationResult result = await App.AuthenticationClient
                .AcquireTokenSilent(Constants.Scopes, accounts.FirstOrDefault())
                .ExecuteAsync();
                
            await Navigation.PushAsync(new LogoutPage(result));
        }
        catch
        {
            // Do nothing - the user isn&#39;t logged in
        }
        base.OnAppearing();
    }

    [...]
}
</code></pre>
<p>Au clic sur le bouton, <code>AcquireTokenInteractive</code> est utilisé pour ouvrir le navigateur de l’appareil et afficher la page de Signin.</p><ul>
<li>Si la connexion réussit le résultat de l’authentification est passé à la page <code>LogoutPage</code>.</li>
<li>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.</li>
</ul>
<pre><code class="language-csharp">private async void LoginButton_Clicked(object sender, EventArgs e)
{
    AuthenticationResult result;
    try
    {
        result = await App.AuthenticationClient
            .AcquireTokenInteractive(Constants.Scopes)
            .WithPrompt(Prompt.SelectAccount)
            .WithParentActivityOrWindow(App.UIParent)
            .ExecuteAsync();

        await Navigation.PushAsync(new LogoutPage(result));
    }
    catch (MsalException ex)
    {
        if (ex.Message != null &amp;&amp; ex.Message.Contains(&quot;AADB2C90118&quot;))
        {
            result = await OnForgotPassword();
            await Navigation.PushAsync(new LogoutPage(result));
        }
        else if (ex.ErrorCode != &quot;authentication_canceled&quot;)
        {
            await DisplayAlert(&quot;An error has occurred&quot;, &quot;Exception message: &quot; + ex.Message, &quot;Dismiss&quot;);
        }
    }
}
</code></pre>
<p>Et voici le code pour la réinitialisation du mot de passe :</p><pre><code class="language-csharp">private async Task&lt;AuthenticationResult&gt; OnForgotPassword()
{
    try
    {
        return await App.AuthenticationClient
            .AcquireTokenInteractive(Constants.Scopes)
            .WithPrompt(Prompt.SelectAccount)
            .WithParentActivityOrWindow(App.UIParent)
            .WithB2CAuthority(Constants.AuthorityPasswordReset)
            .ExecuteAsync();
    }
    catch (MsalException)
    {
        // Do nothing - ErrorCode will be displayed in OnLoginButtonClicked
        return null;
    }
}
</code></pre>
<p>Une fois la page créée, remplacez <code>MainPage</code> dans <code>App</code> par une <code>NavigationPage</code> : <code>MainPage = new NavigationPage(new LoginPage());</code> de façon à naviguer directement sur la <code>LoginPage</code> à l’ouverture de l’application.</p><h3 id="création-dune-page-de-logout">Création d’une page de logout</h3>
<p>La page <code>LogoutPage</code> est très similaire à la page de login à ceci près :</p><ul>
<li>Son constructeur reçoit en paramètre le résultat de l’authentification</li>
<li>Un <code>Label</code> affiche les informations de base de l’utilisateur</li>
<li>Le bouton <code>Login</code> est remplacé par un bouton <code>Logout</code></li>
</ul>
<pre><code class="language-xml">&lt;ContentPage.Content&gt;
    &lt;StackLayout VerticalOptions=&quot;Center&quot; HorizontalOptions=&quot;Center&quot;&gt;
        &lt;Label x:Name=&quot;UserInfoLabel&quot;
                   FontSize=&quot;Large&quot;
                   HorizontalTextAlignment=&quot;Center&quot;
                   /&gt;
        &lt;Button x:Name=&quot;LogoutButton&quot;
                    Text=&quot;LOGOUT&quot;
                    Clicked=&quot;LogoutButton_Clicked&quot;
                    HorizontalOptions=&quot;Center&quot;
                    VerticalOptions=&quot;Center&quot;
                    /&gt;
    &lt;/StackLayout&gt;
&lt;/ContentPage.Content&gt;
</code></pre>
<p>Commençons par récupérer l’<code>AuthenticationResult</code> dans le constructeur de la page :</p><pre><code class="language-csharp">AuthenticationResult authenticationResult;
public LogoutPage(AuthenticationResult result)
{
    InitializeComponent();	
    authenticationResult = result;
}
</code></pre>
<h4 id="les-informations-utilisateur-claims">Les informations utilisateur (Claims)</h4>
<p>Les informations concernant l’utilisateur, celles que vous avez sélectionnées parmi les <code>Claims</code> lors de la création de votre <code>User Flow Signin</code> sont codées dans la propriété <code>authenticationResult.IdToken</code> sous la forme d’un <strong><a href="https://fr.wikipedia.org/wiki/JSON_Web_Token"  class="extlink extlink-icon-1"  >token jwt</a>.</strong>
Pour le décoder, vous aurez besoin d’installer le package nuget <code>System.IdentityModel.Tokens.Jwt</code>.
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/bab1879b2fd647caabcab68ccfaa7a27.png" alt="Package nuget JWT" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/bab1879b2fd647caabcab68ccfaa7a27-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bab1879b2fd647caabcab68ccfaa7a27-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bab1879b2fd647caabcab68ccfaa7a27-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/bab1879b2fd647caabcab68ccfaa7a27-lg.png 1024w"></figure></p><p>Et voici le snippet pour le décoder :</p><pre><code class="language-csharp">var stream = authenticationResult.IdToken;
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken decodedToken = (JwtSecurityToken)handler.ReadToken(stream);
</code></pre>
<p>Vous retrouverez toutes les informations dans une collection d’objet <code>Claim</code>, notamment au travers les propriétés <code>Type</code> et <code>Value</code> :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/85c24b29abca478a83d2c41aa5f504b3.png" alt="Structure des données de Claim" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/85c24b29abca478a83d2c41aa5f504b3-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/85c24b29abca478a83d2c41aa5f504b3-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/85c24b29abca478a83d2c41aa5f504b3-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/85c24b29abca478a83d2c41aa5f504b3-lg.png 1024w"></figure></p><p>Exemple, pour afficher le nom et la ville de l’utilisateur :</p><pre><code class="language-csharp">string name = decodedToken.Claims.FirstOrDefault(c =&gt; c.Type == &quot;name&quot;)?.Value ?? &quot;Noname&quot;;
string city = decodedToken.Claims.FirstOrDefault(c =&gt; c.Type == &quot;city&quot;)?.Value ?? &quot;Unknown city&quot;;

UserInfoLabel.Text = $&quot;Welcome {name}, from {city}&quot;;
</code></pre>
<p>Le code sera appelé de préférence dans le <code>OnAppearing</code> de la page plutôt que dans son constructeur.</p><p>Et voilà le résultat :
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/1050e4916bdf4788b637930fa9fad470.png" alt="Page de Logout" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/1050e4916bdf4788b637930fa9fad470-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/1050e4916bdf4788b637930fa9fad470-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/1050e4916bdf4788b637930fa9fad470-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/1050e4916bdf4788b637930fa9fad470-lg.png 1024w"></figure></p><h3 id="déconnexion">Déconnexion</h3>
<p>La déconnexion consiste principalement à retirer un compte de <code>AuthenticationClient</code> :</p><pre><code class="language-csharp">private async void LogoutButton_Clicked(object sender, EventArgs e)
{
    IEnumerable&lt;IAccount&gt; accounts = await App.AuthenticationClient.GetAccountsAsync();
    while (accounts.Any())
    {
        await App.AuthenticationClient.RemoveAsync(accounts.First());
        accounts = await App.AuthenticationClient.GetAccountsAsync();
    }
    
    await Navigation.PopAsync();
}
</code></pre>
<h3 id="la-page-de-signin">La page de Signin</h3>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/cd085170ebd9443da4c69f613d06d631.png" alt="Page de Signin" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/cd085170ebd9443da4c69f613d06d631-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/cd085170ebd9443da4c69f613d06d631-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/cd085170ebd9443da4c69f613d06d631-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/cd085170ebd9443da4c69f613d06d631-lg.png 1024w"></figure><h3 id="page-de-réinitialisation-de-mot-de-passe">Page de réinitialisation de mot de passe</h3>
<figure class="post__image"><img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/42/ff9ca60e682d4f16acd619ee2d0aa7c6.png" alt="Page de réinitialisation de mot de passe" sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff9ca60e682d4f16acd619ee2d0aa7c6-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff9ca60e682d4f16acd619ee2d0aa7c6-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff9ca60e682d4f16acd619ee2d0aa7c6-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/42/responsive/ff9ca60e682d4f16acd619ee2d0aa7c6-lg.png 1024w"></figure>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Le Xamarin Community Toolkit</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/le-xamarin-community-toolkit/"/>
        <id>https://www.sylvainmoingeon.fr/le-xamarin-community-toolkit/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/41/whatsnewintoolkit.png" medium="image" />
            <category term="toolkit"/>
            <category term="Xamarin.Forms"/>

        <updated>2021-01-27T17:58:25+01:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/41/whatsnewintoolkit.png" alt="" />
                    
  <p>
    Fruit de la collaboration entre l'équipe Xamarin et la communauté Open Source, le <a href="https://github.com/xamarin/XamarinCommunityToolkit" target="_blank">Xamarin Community Toolkit</a> est à <a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/">Xamarin.Forms</a> ce que le sabre laser est au Jedi, c'est-à-dire son indispensable complément.
  </p>

    <figure class="post__image post__image--center">
      <img loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/41/communitytoolkit.png" height="142" width="300" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-lg.png 1024w">
      
    </figure>

  <p>
    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 <a href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/" target="_blank">dotNet MAUI</a>. Mais voyons cela plus en détails.
  </p>

                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/41/whatsnewintoolkit.png" class="type:primaryImage" alt="" /></p>
                
  <p>
    Fruit de la collaboration entre l'équipe Xamarin et la communauté Open Source, le <a href="https://github.com/xamarin/XamarinCommunityToolkit" target="_blank" class="extlink extlink-icon-1"  >Xamarin Community Toolkit</a> est à <a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/">Xamarin.Forms</a> ce que le sabre laser est au Jedi, c'est-à-dire son indispensable complément.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/41/communitytoolkit.png" height="142" width="300" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/41/responsive/communitytoolkit-lg.png 1024w">
      
    </figure>

  <p>
    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 <a href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/" target="_blank" class="extlink extlink-icon-1"  >dotNet MAUI</a>. Mais voyons cela plus en détails.
  </p>


    <h2 id="xamarinforms-50">
      Xamarin.Forms 5.0
    </h2>

  <p>
    <a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/">Xamarin.Forms</a> 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 <a href="https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/" target="_blank" class="extlink extlink-icon-1"  >Microsoft MAUI</a>.
  </p>

  <p>
    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 <a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/views/expander" target="_blank" class="extlink extlink-icon-1"  >Expander</a> ou <a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/views/mediaelement" target="_blank" class="extlink extlink-icon-1"  >MediaElement</a> par exemple) ou dont le développement n'était pas terminé.
  </p>

  <p>
    C'est donc au <a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" target="_blank" class="extlink extlink-icon-1"  >Community Toolkit</a> que revient la charge de toutes les nouveautés de Xamarin.Forms !
  </p>

  <p class="msg msg--highlight">
    Vous comprenez donc bien que pour bénéficier des prochaines évolutions de Xamarin.Forms, il faudra vous tourner vers le toolkit !
  </p>

  <p>
    Mais ce fameux toolkit, que contient-il donc ?
  </p>

    <h2 id="le-xamarin-community-toolkit">
      Le Xamarin Community Toolkit
    </h2>

  <p>
    Je ne vais pas vous donner une liste exhaustive du contenu du toolkit, d'abord parce que tout est disponible dans <a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" target="_blank" class="extlink extlink-icon-1"  >sa documentation</a> 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.
  </p>

    <h3 id="behaviors">
      Behaviors
    </h3>

  <p>
    Fonctionnalité peu connue (ou en tout cas trop peu utilisée) de Xamarin.Forms, les <code>Behaviors</code> 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.
  </p>

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

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

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

    <h3 id="converters">
      Converters
    </h3>

  <p>
    Les <code>Converters</code> 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 !).
  </p>

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

  <p>
    C'est une vingtaine de <code>Converters</code> très utiles qui vous attendent dans le toolkit.
  </p>

    <h3 id="des-tas-de-views-contrles-xaml">
      Des tas de Views (contrôles XAML)
    </h3>

  <p>
    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é.
  </p>

    <h3 id="et-dautres-choses-encore">
      Et d'autres choses encore
    </h3>

  <p>
    Des helpers MVVM, des <code>Effects</code> XAML (cousin des <code>Behaviors</code> mais plus light), des extensions, pour en savoir plus, je vous invite à consulter <a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" target="_blank" class="extlink extlink-icon-1"  >la documentation</a> et les quelques ressources que je donne juste après, là, maintenant.
  </p>

    <h2 id="ressources">
      Ressources
    </h2>

  <p>
    J'ai déjà donné le lien plusieurs fois mais si vous l'avez manqué, dirigez-vous vers la <a href="https://docs.microsoft.com/fr-fr/xamarin/community-toolkit/" target="_blank" class="extlink extlink-icon-1"  >documentation officielle du Xamarin Community Toolkit</a>.
  </p>

  <p>
    Le dépôt GitHub :&nbsp;<a href="https://github.com/xamarin/XamarinCommunityToolkit" target="_blank" class="extlink extlink-icon-1"  >https://github.com/xamarin/XamarinCommunityToolkit</a>
  </p>

  <p>
    Le dépôt de l'application d'exemples :&nbsp;<a href="https://github.com/xamarin/XamarinCommunityToolkit/tree/main/samples" target="_blank" class="extlink extlink-icon-1"  >https://github.com/xamarin/XamarinCommunityToolkit/tree/main/samples</a>
  </p>

  <p>
    L'article de blog de Gerald Versluis :&nbsp;<a href="https://devblogs.microsoft.com/xamarin/xamarin-community-toolkit/" target="_blank" class="extlink extlink-icon-1"  >Xamarin Community Toolkit: A Must-Have Xamarin Library</a>
  </p>

  <p>
    Les vidéos de présentation par deux stars du monde Xamarin (Gerald Versluis et Javier Suarez)
  </p>

  <p>
    <div class="post__iframe"><iframe loading="lazy" width="560" height="315" src="https://www.youtube-nocookie.com/embed/l09fzU0_N04" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div><br>
  </p>

  <p>
    <div class="post__iframe"><iframe loading="lazy" width="560" height="315" src="https://www.youtube-nocookie.com/embed/JmZbfr1590A" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
  </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Planet Xamarin</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/planet-xamarin/"/>
        <id>https://www.sylvainmoingeon.fr/planet-xamarin/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/40/planetxamarin-featured-badge.png" medium="image" />

        <updated>2021-01-26T17:53:14+01:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/40/planetxamarin-featured-badge.png" alt="Logo Planet Xamarin" />
                    Chercher des ressources concernant Xamarin.Forms est parfois un parcours du combattant. C'est&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/40/planetxamarin-featured-badge.png" class="type:primaryImage" alt="Logo Planet Xamarin" /></p>
                
  <p>
    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.
  </p>

  <p>
    Et s'il existait un pays merveilleux dans lequel les meilleurs blogs étaient réunis ?
  </p>

  <p>
    Ce pays, ou plutôt cette planète, existe et s'appelle <a href="https://www.planetxamarin.com/" target="_blank" class="extlink extlink-icon-1"  >Planet Xamarin</a> !
  </p>

  <p>
    Il s'agit d'un agrégateur de blog, s'y abonner, c'est <a href="https://www.planetxamarin.com/feed" target="_blank" class="extlink extlink-icon-1"  >s'abonner au gratin international des blogs Xamarin</a> !
  </p>

  <p>
    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".
  </p>

    <figure class="post__image post__image--center">
      <a href="https://www.planetxamarin.com/" target="_blank">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/40/planetxamarin-featured-badge-2.png" height="180" width="430" alt="Logo de Planet Xamarin"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/40/responsive/planetxamarin-featured-badge-2-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/40/responsive/planetxamarin-featured-badge-2-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/40/responsive/planetxamarin-featured-badge-2-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/40/responsive/planetxamarin-featured-badge-2-lg.png 1024w">
      </a>
      
    </figure>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Utiliser des images vectorielles SVG dans Xamarin.Forms</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/utiliser-des-images-vectorielles-svg-dans-xamarinforms/"/>
        <id>https://www.sylvainmoingeon.fr/utiliser-des-images-vectorielles-svg-dans-xamarinforms/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/39/cover_svg.png" medium="image" />
            <category term="svg"/>
            <category term="Xamarin.Forms"/>
            <category term="XAML"/>

        <updated>2020-11-17T15:55:28+01:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/39/cover_svg.png" alt="" />
                    Il est possible de longue date d'utiliser des bibliothèques tierces telles que&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/39/cover_svg.png" class="type:primaryImage" alt="" /></p>
                
  <p>
    Il est possible de longue date d'utiliser des bibliothèques tierces telles que <a href="https://github.com/mono/SkiaSharp" target="_blank" class="extlink extlink-icon-1"  >Skiasharp</a> pour afficher des images vectorielles dans <a href="https://www.sylvainmoingeon.fr/tags/xamarinforms/">Xamarin.Forms</a>.&nbsp;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.
  </p>

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

  <p>
    Je vous montre comment ça se path... euh... passe ?
  </p>
<hr class="separator separator--dots" />

    <h2 id="convertir-le-contenu-dun-svg-en-path">
      Convertir le contenu d'un SVG en Path
    </h2>

  <p class="msg msg--info">
    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 :&nbsp;<code>Device.SetFlags(new string[] { "Shapes_Experimental" });</code> dans le constructeur de la classe <code>App</code>
  </p>

  <p>
    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.
  </p>

  <p>
    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.
  </p>

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

    <h3 id="image-simple-compose-dun-seul-chemin">
      Image simple composée d'un seul chemin
    </h3>

  <p>
    Nous allons partir de ce svg très simple représentant un avion :
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/39/airport.svg" height="64" width="64" alt="Une image vectorielle simple" >
      <figcaption>Une image vectorielle simple</figcaption>
    </figure>

  <p>
    Si vous ouvrez le fichier avec un éditeur de texte, vous y trouverez ceci :
  </p>
<pre class="line-numbers  language-html"><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;svg enable-background="new 0 0 64 64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"&gt;
    &lt;path d="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"/&gt;
&lt;/svg&gt;
</code></pre>

  <p>
    Ce qui nous intéresse ici, c'est la balise <code>path</code> et en particulier son attribut <code>d</code> contenant le tracé du dessin. Nous allons simplement copier le contenu de <code>d</code> dans la propriété <code>Data</code> d'un <code>Path</code> Xamarin.Forms. Comme ceci :&nbsp;
  </p>
<pre class="line-numbers  language-html"><code>&lt;Path Aspect="Uniform"
      Data="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"
      Fill="Black"
      HeightRequest="64"  /&gt;</code></pre>

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

  <p>
    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.
  </p>

  <p>
    La même image vectorielle agrandie x2, pivotée de 45° avec un fond vert et un contour pointillé noir.
  </p>
<pre class="line-numbers  language-html"><code>&lt;Path Aspect="Uniform"
      Data="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"            
      Fill="Green"
      HeightRequest="64"
      Rotation="45"
      Stroke="Black"
      Scale="2"
      StrokeDashArray="2 1"
      StrokeThickness="2" /&gt;</code></pre>

  <p>
    Le résultat sous Android :<br>
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/39/screenshot-svg-simple-2.png" height="295" width="355" alt="Une image vectorielle, affichée et manipulée dans Xamarin.Forms"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-simple-2-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-simple-2-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-simple-2-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-simple-2-lg.png 1024w">
      <figcaption>Une image vectorielle, affichée et manipulée dans Xamarin.Forms</figcaption>
    </figure>

    <h3 id="image-complexe-compose-de-plusieurs-chemins">
      Image complexe composée de plusieurs chemins
    </h3>

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

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/39/car.svg" height="512" width="512" alt="" >
      
    </figure>
<pre class="line-numbers  language-html"><code>&lt;?xml version="1.0"?&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"&gt;
    &lt;path d="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"/&gt;
    &lt;path d="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"/&gt;
    &lt;path d="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"/&gt;
    &lt;path d="M256,248h40a8,8,0,0,0,0-16H256a8,8,0,0,0,0,16Z"/&gt;
&lt;/svg&gt;
</code></pre>

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

  <p>
    Heureusement, la propriété <code>Data</code> de notre <code>Path</code> 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 <code>GeometryGroup</code> contenant des <code>PathGeometry</code> dont nous renseigneront la propriété <code>Figures</code>.
  </p>

  <p>
    Chaque path de notre svg alimente ainsi les propriétés <code>Figures</code> de nos <code>PathGeometry</code> :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Path Aspect="Uniform"
      Fill="Black"
      HeightRequest="64"&gt;
    &lt;Path.Data&gt;
        &lt;GeometryGroup&gt;
            &lt;PathGeometry Figures="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" /&gt;
            &lt;PathGeometry Figures="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" /&gt;
            &lt;PathGeometry Figures="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" /&gt;
            &lt;PathGeometry Figures="M256,248h40a8,8,0,0,0,0-16H256a8,8,0,0,0,0,16Z" /&gt;
        &lt;/GeometryGroup&gt;
    &lt;/Path.Data&gt;
&lt;/Path&gt;
</code></pre>

  <p>
    Et hop :
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/39/screenshot-svg-complexe.png" height="132" width="353" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-complexe-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-complexe-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-complexe-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-complexe-lg.png 1024w">
      
    </figure>

  <p>
    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 <code>Path</code> à chaque fois.&nbsp;
  </p>

  <p id="voyons-comment-rsoudre-ce-problme-avec-style-">
    Voyons comment résoudre ce problème avec Style !
  </p>

    <h2 id="crer-une-image-vectorielle-rutilisable-grce-aux-styles">
      Créer une image vectorielle réutilisable grâce aux styles
    </h2>

  <p>
    Nous allons créer un&nbsp;<code>ResourceDictionary</code> pour y placer des <code>Styles</code> définissant le tracé de chaque image vectorielle.<resourcedictionary <br=""></resourcedictionary>
  </p>

  <p>
    Toutes nos images SVG auront des propriétés communes, nous allons donc commencer par créer un style de base :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Style x:Key="BaseSVGStyle" TargetType="Path"&gt;
    &lt;Setter Property="Aspect" Value="Uniform" /&gt;
    &lt;Setter Property="Fill" Value="Black" /&gt;
&lt;/Style&gt;</code></pre>

  <p>
    Nous allons ensuite créer un style par image héritant de <code>BaseSVGStyle</code>&nbsp;à l'aide de la propriété <code>BasedOn</code>. Voici un exemple :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Style x:Key="PlaneSVG"
        BasedOn="{StaticResource BaseSVGStyle}"
        TargetType="Path"&gt;
    &lt;Setter Property="Data" Value="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" /&gt;
    &lt;Setter Property="HeightRequest" Value="64" /&gt;
&lt;/Style&gt;
    
&lt;Style x:Key="CarSVG"
        BasedOn="{StaticResource BaseSVGStyle}"
        TargetType="Path"&gt;
    &lt;Setter Property="HeightRequest" Value="32" /&gt;
    &lt;Setter Property="Data"&gt;
        &lt;Setter.Value&gt;
            &lt;GeometryGroup&gt;
                &lt;PathGeometry Figures="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" /&gt;
                &lt;PathGeometry Figures="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" /&gt;
                &lt;PathGeometry Figures="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" /&gt;
                &lt;PathGeometry Figures="M256,248h40a8,8,0,0,0,0-16H256a8,8,0,0,0,0,16Z" /&gt;
            &lt;/GeometryGroup&gt;
        &lt;/Setter.Value&gt;
    &lt;/Setter&gt;
&lt;/Style&gt;</code></pre>

  <p>
    Les styles consistent donc principalement à définir la valeur de la propriété <code>Data</code> de nos <code>Path</code>.
  </p>

  <p>
    Pour les utiliser dans nos <code>Views</code>, nous devons d'abord déclarer le dictionnaire de ressource. Soit dans les ressources de la page, soit dans <code>App.xaml</code> si l'on souhaite accéder aux images partout dans le projet (<em>PathesStyles</em> est le nom de mon dictionnaire de ressource).
  </p>
<pre class="line-numbers  language-html"><code>&lt;ContentPage.Resources&gt;
    &lt;ResourceDictionary&gt;
        &lt;ResourceDictionary.MergedDictionaries&gt;
            &lt;pathes:PathesStyles /&gt;
        &lt;/ResourceDictionary.MergedDictionaries&gt;
    &lt;/ResourceDictionary&gt;
&lt;/ContentPage.Resources&gt;</code></pre>

  <p>
    Ensuite, dans la contenu de la page, c'est simple comme <i>Bonjour</i>&nbsp;, on applique le style souhaité à notre path :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Path Style="{StaticResource CarSVG}" /&gt;</code></pre>

  <p>
    Il est toujours possible, bien entendu, de surcharger les valeurs proposées par le style. Pour afficher l'image en rouge par exemple :&nbsp;
  </p>
<pre class="line-numbers  language-html"><code>&lt;Path Style="{StaticResource CarSVG}" Fill="Red" /&gt;</code></pre>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/39/screenshot-svg.png" height="1250" width="608" alt="Un florilège d'images vectorielles dans Xamarin.Forms"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/39/responsive/screenshot-svg-lg.png 1024w">
      <figcaption>Un florilège d'images vectorielles dans Xamarin.Forms</figcaption>
    </figure>

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

    <h2 id="projet-github">
      Projet GitHub
    </h2>

  <p>
    Retrouvez la <a href="https://github.com/SylvainMoingeon/SVGDemo" target="_blank" class="extlink extlink-icon-1"  >démonstration de l'utilisation des Path Xamarin.Forms pour afficher des images vectorielles</a> dans mon dépôt GitHub.
  </p>

  <p>
    Pensez bien à liker et partager pour que ce soit utile au plus grand nombre !
  </p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Créer un contrôle réutilisable 100% Xamarin.Forms, partie 2</title>
        <author>
            <name>Sylvain</name>
        </author>
        <link href="https://www.sylvainmoingeon.fr/creer-un-controle-reutilisable-100-xamarinforms-partie-2/"/>
        <id>https://www.sylvainmoingeon.fr/creer-un-controle-reutilisable-100-xamarinforms-partie-2/</id>
        <media:content url="https://www.sylvainmoingeon.fr/media/posts/37/micro-assembly.jpg" medium="image" />
            <category term="Xamarin.Forms"/>
            <category term="XAML"/>
            <category term="MVVM"/>
            <category term="From scratch"/>

        <updated>2020-11-10T18:22:28+01:00</updated>
            <summary type="html">
                <![CDATA[
                        <img src="https://www.sylvainmoingeon.fr/media/posts/37/micro-assembly.jpg" alt="" />
                    Vous apprendrez notamment à créer des propriétés bindables (BindableProperty) et à utiliser&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                    <p><img src="https://www.sylvainmoingeon.fr/media/posts/37/micro-assembly.jpg" class="type:primaryImage" alt="" /></p>
                
  <p>
    
  </p>

  <p>
    Vous apprendrez notamment à créer des propriétés bindables (<code><a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/bindable-properties" target="_blank" class="extlink extlink-icon-1"  >BindableProperty</a></code>) et à utiliser les <code><a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/converters" target="_blank"  data-link-popup-id="1604422655301" class=" extlink extlink-icon-1"  >Converters</a></code>.&nbsp;
  </p>

  <p>
    Si ce n'est pas déjà fait, je vous invite à d'abord consulter la <a href="https://www.sylvainmoingeon.fr/creer-un-controle-reutilisable-100-xamarinforms-partie-1/">première partie de cette article</a>&nbsp;dans laquelle nous avons construit les bases d'une image circulaires avec quelques options (placeholder, bordure...).
  </p>
<hr class="separator separator--dots" />

    <h2 id="mais-o-vaton-">
      Mais où va-t-on ?
    </h2>

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

  <p class="msg msg--highlight">
    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, <strong>un seul code pour générer deux applications natives</strong>. C'est la beauté de Xamarin.Forms.
  </p>

  <p>
    &nbsp;On y voit :&nbsp;
  </p>

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

  <div  class="gallery-wrapper">
    <div class="gallery" data-columns="3">
      <figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/37/gallery/demo-circleimage.gif" data-size="350x726">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/gallery/demo-circleimage.gif" height="726" width="350" alt="Screenshot animé sous Android" >
      </a>
      <figcaption>L'image circulaire en situation sous Android</figcaption>
    </figure><figure class="gallery__item">
      <a href="https://www.sylvainmoingeon.fr/media/posts/37/gallery/circleimage-ios-screenshot.png" data-size="1125x2436">
        <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/gallery/circleimage-ios-screenshot-thumbnail.png" height="1663" width="768" alt="Screenshot sous iOS" >
      </a>
      <figcaption>Copie d'écran sous iOS</figcaption>
    </figure>
    </div>
  </div>

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

  <p>
    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.
  </p>

    <h2 id="cration-du-contrle">
      Création du contrôle
    </h2>

    <h3 id="composons">
      Composons
    </h3>

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

  <p>
    Nous avons donc une base avec une image circulaire simple :&nbsp;
  </p>
<pre class="line-numbers  language-html"><code>&lt;Image Source="monimage.png" WidthRequest="128" HeightRequest="128"&gt;
    &lt;Image.Clip&gt;
        &lt;EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64" /&gt;
    &lt;/Image.Clip&gt;
&lt;/Image&gt;</code></pre>

  <p>
    Et une image circulaire avancée, composée à partir de la précédente :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Grid&gt;
    &lt;Image Source="monPlaceholder.png" WidthRequest="128" HeightRequest="128"&gt;
        &lt;Image.Clip&gt;
            &lt;EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64" /&gt;
        &lt;/Image.Clip&gt;
    &lt;/Image&gt;

    &lt;Image Source="monimage.png" WidthRequest="128" HeightRequest="128"&gt;
        &lt;Image.Clip&gt;
            &lt;EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64" /&gt;
        &lt;/Image.Clip&gt;
    &lt;/Image&gt;

    &lt;Ellipse Margin="0"
             HorizontalOptions="Center"
             VerticalOptions="Center"
             Stroke="Yellow"
             StrokeThickness="2"
             HeightRequest="128"
             WidthRequest="128"
             /&gt;

     &lt;ActivityIndicator IsRunning="{Binding Source={x:Reference monImage}, Path=IsLoading}"
                        VerticalOptions="Center"
                        HorizontalOptions="Center"
                        /&gt;
&lt;/Grid&gt;</code></pre>

  <p>
    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 ?
  </p>

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

  <p>
    Quelque chose comme ça :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Grid&gt;
    &lt;local:CircleImage x:Name="monPlaceholder"
                         Source="monPlaceholder.png"
                         ImageSize="128" /&gt;
                         
    &lt;local:CircleImage x:Name="monImage"
                         Source="monImage.png"
                         ImageSize="128" /&gt;

    [...] Le reste du contrôle avec la bordure et l'activityIndicator
&lt;/Grid&gt;</code></pre>

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

  <p>
    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é <code>ImageSize</code> bindable de façon à pouvoir écrire quelque chose comme :
  </p>
<pre class="line-numbers  language-csharp"><code>&lt;local:CircleImage x:Name="monPlaceholder"
                     Source="{Binding MonImage}"
                     ImageSize="{Binding MyImageSize}" /&gt;</code></pre>

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

  <p>
    Si ceci vous parait obscur, relisez mon article sur l'<a href="https://www.sylvainmoingeon.fr/xamarinforms-a-quoi-ca-sert-mvvm/">architecture MVVM</a> !
  </p>

    <h3 id="crer-un-nouveau-contrle">
      Créer un nouveau contrôle
    </h3>

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

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/add-content-view.png" height="778" width="956" alt="Copie d'écran pour illustrer l'ajout d'un ContentView dans Xamarin.Forms"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/37/responsive/add-content-view-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/add-content-view-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/add-content-view-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/add-content-view-lg.png 1024w">
      <figcaption>Ajoutez un ContentView nommé CircleImage</figcaption>
    </figure>

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

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

  <p>
    Attention ! Pensez bien à le faire à la fois dans le XAML...<br>
  </p>
<pre class="line-numbers  language-csharp"><code>&lt;!-- Remplacer --&gt;
&lt;ContentView x:Class="CircleImageDemo.CircleImage.CircleImage"
&lt;-- Par --&gt;
&lt;Image x:Class="CircleImageDemo.CircleImage.CircleImage"</code></pre>

  <p>
    ...et dans le code-behind C# !
  </p>
<pre class="line-numbers  language-csharp"><code>// Remplacer
public partial class CircleImage : ContentView { }
// Par
public partial class CircleImage : Image { }</code></pre>

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

    <h3 id="exposer-des-proprits-bindables-depuis-lextrieur">
      Exposer des propriétés bindables depuis l'extérieur
    </h3>

  <p>
    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.&nbsp;Il s'agit principalement de définir une propriété qui encapsule une <code>BindableProperty</code> au lieu d'un champs privé comme cela se fait habituellement.
  </p>

  <p>
    Je vous vois froncer les sourcils, alors un exemple :&nbsp;
  </p>
<pre class="line-numbers  language-csharp"><code>#region ImageSize Bindable property
// Bindable property
public static readonly BindableProperty ImageSizeProperty =
  BindableProperty.Create(
     propertyName: nameof(ImageSize),
     declaringType: typeof(CircleImage),
     returnType: typeof(double),
     defaultValue: 0.0,
     defaultBindingMode: BindingMode.OneWay,
     propertyChanged: (bindable, oldValue, newValue) =&gt;
     { });

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

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

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

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

  <p>
    Seuls les trois premiers arguments sont obligatoires.
  </p>

  <p>
    Tout ceci se place bien entendu dans le code-behind de la <code>View</code>.
  </p>

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

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

  <p>
    Je vais vous présenter les deux mais, bien entendu, dans le <a href="https://github.com/SylvainMoingeon/CircleImageDemo" target="_blank" class="extlink extlink-icon-1"  >projet GitHub</a> je n'ai pu en laisser qu'un !
  </p>

    <h3 id="lier-les-proprits-bindables-au-xaml-du-contrle">
      Lier les propriétés bindables au XAML du contrôle
    </h3>

  <p>
    Partons de notre <code>CircleImage</code> de base.&nbsp;
  </p>
<pre class="line-numbers  language-html"><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;Image x:Class="CircleImageDemo.CircleImage.CircleImage"
       xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       HeightRequest="128"
       WidthRequest="128"&gt;
    &lt;Image.Clip&gt;
        &lt;EllipseGeometry RadiusX="64"
                         RadiusY="64"
                         Center="64,64"
                         /&gt;
    &lt;/Image.Clip&gt;
&lt;/Image&gt;</code></pre>

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

  <p>
    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.
  </p>

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

  <p>
    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 :
  </p>
<pre class="line-numbers  language-html"><code>&lt;Image x:Name="this" BindingContext="{x:Reference this}" ...&gt;
</code></pre>

  <p>
    On peut désormais binder <code>ImageSize</code> aux propriétés de l'<code>Image</code> :<br><br>
  </p>
<pre class="line-numbers  language-html"><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;Image x:Name="this"
       [...]
       BindingContext="{x:Reference this}"
       HeightRequest="{Binding ImageSize}"
       WidthRequest="{Binding ImageSize}"
       &gt;
    [...]
&lt;/Image&gt;</code></pre>

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

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

  <p>
    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 <code>Converter</code>.
  </p>

  <p>
    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 <code>IValueConverter</code>, 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 <code>DividerConverter</code> !
  </p>

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

  <p>
    La méthode <code>Convert()</code> divise la valeur reçue de l'extérieur, la méthode <code>ConvertBack()</code> 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 !)
  </p>
<pre class="line-numbers  language-csharp"><code>public class DoubleDividerConverter : IValueConverter
{      
    public int Divider { get; set; } = 1;

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

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

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

        throw new ArgumentException($"{nameof(value)} should be of type double");
    }
}
</code></pre>

  <p>
    Reste à utiliser le Converter dans le XAML :
  </p>

  <ul>
    <li>Le déclarer comme ressource statique dans la <code>View</code></li><li>L'ajouter aux Bindings</li><li>Préciser à l'<code>EllipseGeometry</code> son contexte de Binding.</li>
  </ul>
<pre class="line-numbers  language-csharp"><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;Image x:Name="this"
         [...]
       &gt;
    &lt;Image.Resources&gt;
        &lt;ResourceDictionary&gt;
            &lt;local:DoubleDividerConverter 
                    x:Key="DoubleDividerConverter" 
                    Divider="2" /&gt;
        &lt;/ResourceDictionary&gt;
    &lt;/Image.Resources&gt;

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

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

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

  <p>
    Vous trouverez tout le code dans le <a href="https://github.com/SylvainMoingeon/CircleImageDemo" target="_blank" class="extlink extlink-icon-1"  >dépôt GitHub</a>.&nbsp;
  </p>

    <h3 id="grer-la-proprit-bindable-dans-le-code-behind">
      Gérer la propriété bindable dans le code behind
    </h3>

  <p>
    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.
  </p>

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

  <p>
    Et c'est tout !
  </p>
<pre class="line-numbers  language-csharp"><code>public static readonly BindableProperty ImageSizeProperty =
  BindableProperty.Create(
     propertyName: nameof(ImageSize),
     declaringType: typeof(CircleImage),
     returnType: typeof(double),
     defaultValue: 0.0,
     defaultBindingMode: BindingMode.OneWay,
     propertyChanged: (bindable, oldValue, newValue) =&gt;
     {
         if (bindable is CircleImage circleImage && newValue is double imageSize)
         {
             double radius = imageSize / 2;
             EllipseGeometry ellipseGeometry = new EllipseGeometry()
             {
                 Center = new Point(radius, radius),
                 RadiusX = radius,
                 RadiusY = radius
             };

             circleImage.HeightRequest = imageSize;
             circleImage.WidthRequest = imageSize;
             circleImage.Clip = ellipseGeometry;
         }
     });
</code></pre>

  <p>
    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.
  </p>

  <p>
    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 !
  </p>

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

  <p class="msg msg--highlight">
    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 !
  </p>

    <h2 id="appliquons-le-mme-principe-pour-crer-un-contrle-avanc">
      Appliquons le même principe pour créer un contrôle avancé
    </h2>

  <p>
    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.
  </p>

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

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

  <p>
    A l'intérieur de notre Grid, nous aurons donc :&nbsp;
  </p>

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

  <p>
    Dans le code-behind, nous définirons quelques <code>BindablePropertie</code> :
  </p>

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

  <p>
    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 :
  </p>
<pre class="line-numbers  language-csharp"><code>&lt;Grid x:Name="this"&gt;
    &lt;Grid.Resources&gt;
        &lt;DoubleDividerConverter x:Key="DoubleDividerConverter" Divider="2" /&gt;
        &lt;ImageSizeToCenterConverter x:Key="ImageSizeToCenterConverter" /&gt;
        &lt;ColorToBrushConverter x:Key="ColorToBrushConverter" /&gt;
    &lt;/Grid.Resources&gt;

    &lt;local:CircleImage BindingContext="{x:Reference this}"
                       ImageSize="{Binding ImageSize}"
                       Source="{Binding PlaceholderImageSource}"/&gt;

    &lt;local:CircleImage x:Name="Image"
					 BindingContext="{x:Reference this}"
                     ImageSize="{Binding ImageSize}"
                     Source="{Binding MainImageSource}" /&gt;

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

    &lt;ActivityIndicator IsRunning="{Binding Source={x:Reference Image}, Path=IsLoading}"
                       BindingContext="{x:Reference this}"
                       IsVisible="{Binding IsLoaderEnabled}"
                       Color="{Binding BorderColor}"/&gt;
&lt;/Grid&gt;</code></pre>

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

  <p>
    Pour le reste, ce n'est que du <code>Binding</code> vers les <code>BindableProperty</code> et des astuces que nous avons déjà vu dans la <a href="https://www.sylvainmoingeon.fr/creer-un-controle-reutilisable-100-xamarinforms-partie-1/">première partie de l'article</a>.
  </p>

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

  <p>
    Nous en avons même deux : <code>CircleImage</code> et <code>AdvancedCircleImage</code>&nbsp;!
  </p>

    <h2 id="utiliser-le-contrle-dans-le-code">
      Utiliser le contrôle dans le code
    </h2>

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

  <p>
    Il suffit de :
  </p>

  <ol>
    <li>Déclarer le namespace auquel appartient votre contrôle dans votre <code>ContentPage</code></li><li>Utiliser le contrôle dans le XAML</li>
  </ol>

  <p>
    Voici un exemple très simplifié :
  </p>
<pre class="line-numbers  language-csharp"><code>&lt;ContentPage x:Class="CircleImageDemo.MainPage"
            xmlns:ci="clr-namespace:CircleImageDemo.CircleImage"
            [...]&gt;            
    &lt;ci:AdvancedCircleImage ImageSize="64"
                            MainImageSource="{Binding PhotoUrl}"
                            PlaceholderImageSource="{Binding PlaceholderImage}"
                            BorderColor="Yellow"
                            BorderThickness="4"
                            IsBorderVisible="{Binding IsBookmarked}"
                            IsLoaderEnabled="True" /&gt;
&lt;/ContentPage&gt;
</code></pre>

  <p>
    Dans le <a href="https://github.com/SylvainMoingeon/CircleImageDemo" target="_blank" class="extlink extlink-icon-1"  >projet de démonstration</a>, j'utilise l'<code>AdvancedCircleImage</code> dans une liste (en fait un <a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/bindable-layouts" target="_blank" class="extlink extlink-icon-1"  >BindableLayout</a> mais ce n'est pas le sujet).
  </p>

    <h2 id="le-projet-de-dmonstration">
      Le projet de démonstration
    </h2>

  <p>
    Je vous redonne le lien vers le dépôt GitHub du projet :&nbsp;<a href="https://github.com/SylvainMoingeon/CircleImageDemo" target="_blank" class="extlink extlink-icon-1"  >https://github.com/SylvainMoingeon/CircleImageDemo</a>&nbsp;ainsi que la petite animation :
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/demo-circleimage-2.gif" height="726" width="350" alt="" >
      
    </figure>

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

  <p>
    L'<code>AdvancedCircleImage</code> 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.
  </p>

  <p>
    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.
  </p>

  <p>
    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).
  </p>

    <h3 id="jetezy-un-il-">
      Jetez-y un œil !
    </h3>

  <p>
    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 <a href="https://www.sylvainmoingeon.fr/xamarinforms-a-quoi-ca-sert-mvvm/">MVVM</a>. Et si vous êtes arrivé jusqu'à la fin de cet article, c'est sans doute le cas !
  </p>

    <h4 id="stringformat">
      StringFormat
    </h4>

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

    <h4 id="code--xaml-spcifique--la-plateforme">
      Code / XAML spécifique à la plateforme
    </h4>

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

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

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/no-safe-area-2.png" height="915" width="422" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/37/responsive/no-safe-area-2-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/no-safe-area-2-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/no-safe-area-2-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/no-safe-area-2-lg.png 1024w">
      
    </figure>

  <p>
    Il existe quelques fonctionnalités spécifiques aux plateformes directement accessibles dans le code commun, notamment pour <a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/ios/" target="_blank" class="extlink extlink-icon-1"  >iOS</a> et <a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/android/" target="_blank" class="extlink extlink-icon-1"  >Android</a>. Ici, nous allons nous intéresser au <code>SafeArea</code> d'iOS.
  </p>

  <p>
    Dans le XAML :
  </p>
<pre class="line-numbers  language-html"><code>&lt;ContentPage 
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="True"
[...]&gt;
    [...]
&lt;/ContentPage&gt;</code></pre>

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

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/safe-area.png" height="915" width="422" alt=""  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/37/responsive/safe-area-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/safe-area-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/safe-area-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/safe-area-lg.png 1024w">
      
    </figure>

  <p class="msg msg--highlight">
    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.
  </p>

    <h4 id="designtimevisible">
      DesignTimeVisible
    </h4>

  <p>
    Si vous utilisez le <a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-previewer/?pivots=windows" target="_blank" class="extlink extlink-icon-1"  >Previewer Xamarin.Forms</a>, 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.
  </p>

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/designtime-not-visible.png" height="811" width="981" alt="Copie d'écran présentant le previewer Xamarin.Forms, le contrôle personnalisé n'est pas affiché"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-not-visible-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-not-visible-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-not-visible-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-not-visible-lg.png 1024w">
      <figcaption>L'image circulaire n'est pas affichée pas dans le previewer, quel malheur !</figcaption>
    </figure>

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

  <p>
    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.
  </p>
<pre class="line-numbers  language-csharp"><code>[XamlCompilation(XamlCompilationOptions.Compile)]
[DesignTimeVisible(true)]
public partial class AdvancedCircleImage : Grid
{
[...]
}</code></pre>

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

    <figure class="post__image post__image--center">
      <img decoding="auto" loading="lazy" src="https://www.sylvainmoingeon.fr/media/posts/37/designtime-visible.png" height="758" width="949" alt="Illustration du previewer avec l'attribut DesignTimeVisible"  sizes="(max-width: 1024px) 100vw, 1024px" srcset="https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-visible-xs.png 300w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-visible-sm.png 480w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-visible-md.png 768w ,https://www.sylvainmoingeon.fr/media/posts/37/responsive/designtime-visible-lg.png 1024w">
      <figcaption>L'image circulaire est bien affichée grâce à l'attribut DesignTimeVisible(true)</figcaption>
    </figure>

    <h2 id="conclusion">
      Conclusion
    </h2>

  <p>
    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 !
  </p>

  <p>
    Si vous avez des questions, des astuces ou même des remontrances concernant le sujet, je vous invite à laisser des commentaires. Juste là dessous. ⬇
  </p>
            ]]>
        </content>
    </entry>
</feed>
