WPF impose une nouvelle conduite de projets. En effet, maintenant avec cette technologie émergente, les designers et les programmeurs travaillent en commun. Un problème m'est survenu dans l'intégration du design. Prenons l'exemple d'un Canvas pour qui sa propriété Clip est passée entre les mains d'un designer afin de lui donner une forme bien précise, en utilisant des courbes de Béziers, des arcs ou encore des quadratiques de Béziers. Si on se penche sur la valeur de cette propriété, on peut remarquer que c'est une simple chaîne de caractères, avec une suite de chiffres et de lettres. Le designer a en fait généré un PathGeometry qui définit le contour du composant. La problématique était de rendre vectoriel quelques rectangles intégrant dans leur propriété Fill, un VisualBrush qui encapsulait une image. Ces rectangles, superposés les uns sur les autres, avaient tous un contour différents et par conséquent, leur propriété Clip n'avait pour valeur q'une chaîne de caractères du style :

M12.23254;65.324254A12;32 . . . L0.1453;9.345z

Par conséquent, impossible d'appliquer la méthode Transform sur la propriété. Cette chaîne à première vue complexe et alambiquée, ne l'ai qu'au premier abord. Il faut savoir que le PathGeometry possède une alternative plus légère : le StreamGeometry. Cette classe possède quelques contraintes, car elle ne prend pas en charge les liaisons de données, les animations ni les modifications. Cependant cette classe de type Geometry permet d'être tracée ou décrite facilement avec l'aide d'un StreamGeometryContext qui utilise des commandes de dessins. On peut assimiler un StreamGeometryContext comme une sorte de sérialiseur de tracés. Cette classe expose 5 principales méthode énoncée ci-dessous. Chacune de ces méthode permet de décrire un tracé précis, leur nom est assez explicite pour que je ne vous les détaille pas. L'appel à la méthode Freeze() est optionnel, il permet une optimisation en terme de performances.

StreamGeometryHelper_UsingStreamGeometryContex.jpg



Maintenant que nous avons posé les bases, nous allons rentrer dans la décomposition de cette chaîne de caractères qui semble s'étendre à l'infini :).

Pour commencer, on peut remarquer la présence très ponctuelle des lettres. En fait, elles sont considérées comme des marqueurs de type du tracé. On peut en dénombrer 5, tout comme le nombre de méthodes de tracés de la classe StreamGeometryContext. Voici les 5 marqueurs différents avec leur méthode respective :

  • A ArcTo().
  • C BezierTo().
  • L LineTo().
  • M BeginFigure().
  • Q QuadricBezierTo()

On pourra remarquer la présence d'un autre marqueur : z, qui indique simplement la fin de la valeur. Le reste de la chaîne de caractères correspond aux points, plus précisément aux paramètres, qui permettent de définir le tracé. La méthode BeginFigure(), prend en paramètre le point d'entré du tracé. Cette méthode et par extension son marqueur M est indispensable. S'il est absent le dessin ne pourra pas s'effectuer. Les marqueurs C, L et Q sont suivit d'une liste de points. Chaque point est sérialisé sous la forme : <double>X;<double>Y, avec un espace qui sépare chacun d'entre eux. Pour les arcs, les paramètres diffèrent un peut. C'est pour cela que l'on trouvera 1 marqueur A par arc de tracé contrairement aux trois autres qui sont unique dans toute la valeur. Voici le découpage de la sérialisation d'un arc : <double>Width;<double>Height;<double>Angle;<bool>IsLargeArc;<enum>SweepDirection;<double>X;<double>Y. L'ordre d'apparition des marqueurs et par conséquent de leurs paramètres est aléatoire, hormis le marqueur M qui sera toujours en tête de la string. On pourra trouver de temps à autre un marqueur F1 qui doit caractériser que l'on est passé par la création d'un PathGeometry plutôt q'un StreamGeometry, mais je n'en suis pas persuadé.

Passons maintenant coeur du sujet : voici le code

StreamGeometryHelper_Code.jpg