Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Les graphiques en action sous MAUI

Depuis les deux derniers articles je sens une demande plus pressante sur les fameux graphiques de MAUI… Alors comment résister… Voici comment faire des dessins sous MAUI !

Les graphiques sous MAUI

Dire que j’étais parti juste pour faire voir comment ajouter une ombre… Et nous voici trois articles plus loin à vouloir détailler comment dessiner sous MAUI… Je sentais le piège venir pour être francs. Les ombres c’est sympa mais il y avait tellement plus à dire. D’où le second article. Et dans celui-ci je présentais la vue GraphicsView. Là encore je sentais le piège se refermer doucement mais sûrement… Comment évoquer une telle avancée sans en présenter les véritables avantages avec un exemple concret ?

Alors nous y voici. Le principe va rester simple car en matière de dessin d’une part je ne suis pas graphiste et d’autre part cela débouche toujours sur un code trop long ce que je veux éviter (c’est enquiquinant à écrire, et c’est rasoir pour le lecteur).

Le plus simple que j’ai trouvé pour illustrer le mécanisme de GraphicsView c’est un bouton de compte à rebours. Un simple décompte de 5 à 0. Mais entouré d’un joli cercle qui décroit avec le temps… 

Et ça ressemble à cela :

 


Etape 1 – Faire un bouton rond

Ce n’est vraiment pas le plus difficile et cela n’a rien à voir avec les nouvelles possibilités graphiques donc on va aller vite. En XAML ça donnera cela :


<Button x:Name="ProgressButton"
                    Text="&#xf144;"
                    FontSize="32"
                    FontFamily="fa-regular"
                    HeightRequest="80"
                    WidthRequest="80"
                    CornerRadius="40"
                    HorizontalOptions="Center"
                    Clicked="StartButton_OnClicked"
                    BorderWidth="0"
                    BackgroundColor="{StaticResource Primary}"/>


Le gestionnaire du Click a été créé mais il est encore vide, quant à la couleur de fond, on pourrait la définir de beaucoup d’autres façon bien entendu. Pour faire plus joli j’utilise Font Awesome en version free. Cela me permet d’avoir un look plus sympa pour les chiffres et surtout d’avoir le symbole « Play » au départ pour lancer le compte à rebours (c’est l’unicode f1444 qu’on voit dans le texte du bouton).

Etape 2 – le code

Là aussi je passe vite ce n’est pas l’objet. Forcément il faut du code au moins pour répondre au click sur le bouton, la méthode Startbutton_Clicked qu’on voit dans le code XAML plus haut.

S’agissant d’un décompte partant de 5, on trouve une mesure du temps de départ et dans une boucle asynchrone la comparaison entre le temps actuel et celui de départ pour savoir où on en est et afficher le nombre de secondes restantes ainsi que mettre à jour le graphique !

Etape 3 – Le graphique

On est venu pour ça. On y est.

L’animation qui suit montre la cinématique globale du contrôle : sont état d’attente avec le symbole « play », le click sur le bouton, le décompte jusqu’à zéro, le cercle autour du bouton qui couvre de moins en moins de degrés, et un second bouton « reset » sous le décompte qui permet de remettre à zéro le tout et de recommencer !


L’objet graphique

Pour dessiner il faut créer une classe qui implémente IDrawable. En dehors de la méthode Draw c’est tout ce que cette interface oblige à implémenter. Tout le code peut donc être dans cette méthode ou bien la classe peut être très garnie et effectuer des choses plus complexes ou décomposer un dessin en plusieurs parties avant de les assembler dans Draw. 

Ici le code va être le suivant pour cette classe :

public class ProgressArc : IDrawable
{
    public double Progress { get; set; } = 100;
    public void Draw(ICanvas canvas, RectF dirtyRect)
    {
        // Angle en degrés
        var endAngle = 90 - (int)Math.Round(Progress * 360, MidpointRounding.AwayFromZero);
        canvas.StrokeColor = Color.FromRgba("6599ff");
        canvas.StrokeSize = 4;
        canvas.DrawArc(5, 5, (dirtyRect.Width - 10), (dirtyRect.Height - 10), 
                                 90, endAngle, false, false);
    }
}


La classe expose une propriété, Progress, dont la valeur ira de 100 à 0. Elle sera alimenté de l’extérieur ce que nous verrons juste après. Dans la méthode Draw on trouve en premier lieu le calcul de l’angle à dessiner. Il dépend bien entendu de la progression.

Ensuite le canvas est utilisé pour enfin dessiner : la taille du stroke, sa couleur etc, et in fine un appel à DrawArc qui dessine un arc de cercle. 

Tout cela est merveilleux mais cette classe isolée, comment fait elle pour se mettre à jour et surtout pour se dessiner sur l’écran ?

La réponse à la première question est dans le code. Quand le bouton est cliqué, il appelle une méthode asynchrone d’update du cercle, à l’intérieur de celle-ci se trouve une boucle qui teste le nombre de secondes restantes et qui se termine quand cette valeur atteint zéro. Dans la boucle, outre un délai de 500 ms (await Task.Delay(500) ) il y a le calcul de la progression et la mise à jour de la propriété Progress de la classe de dessin (ci-dessus).

La réponse à la seconde question fait intervenir XAML et sa vue GraphicsView.

<GraphicsView x:Name="ProgressView"
                          BackgroundColor="{StaticResource Primary}"
                          HeightRequest="100"
                          WidthRequest="100"/>

Placée juste avant le bouton et dans une même grille (pour que les deux objets visuels se trouvent l’un sur l’autre) la GraphicsView semble être juste posée là sans lien avec la séquence de dessin.

C’est que tout simplement, à la création de page, on crée une instance de notre objet de dessin et on l’affect à la propriété Drawable de la GraphicsView :

protected override void OnAppearing()
    {
        base.OnAppearing();
        ProgressView.Drawable = progressArc;
    }

Et voilà, le lien est fait !

Pour résumer : on crée un objet de dessin héritant de l’interface IDrawable. On fait en sorte que le rendu du dessin se fasse dans sa méthode Draw. Ensuite on place sur la page un GraphicsView dont on initialise la propriété Drawable avec l’instance de l’objet de dessin. 

Le mécanisme n’est pas aussi direct qu’on pourrait le souhaiter, mais il a l’énorme avantage d’être simple, efficace et surtout ne l’oublions pas, d’être totalement cross-plateforme sans rien avoir à connaître des dessins natifs sous iOS, Windows, Mac, etc… !

Conclusion

La GraphicsView est certes moins pratique que de véritables primitives graphiques XAML comme on en trouve sous WPF. Mais ce dernier a été conçu pour fonctionner sous Windows uniquement et juste avec DirectX. MAUI est conçu pour tourner partout, sur des tas d’OS et sans que vous ayez à connaître les détails techniques de ces derniers.

La perfection viendra un jour, mais un grand pas a été franchi avec MAUI dans la bonne direction, celle qui vous permet aujourd’hui de dessiner de façon native en cross-plateforme tout en n'étant pas des spécialistes de tous les différents OS !

Stay Tuned !

Faites des heureux, partagez l'article !
blog comments powered by Disqus