Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Thomas Lebrun

Tout sur WPF, LINQ, C# et .NET en général !

[WPF] Développer un contrôle permettant l'affichage de graphiques (histograme, etc....) avec WPF

Au travers de ce post, je vais m'attacher à vous expliquer comment utiliser WPF (Windows Presentation Foundation) pour développer un contrôle permettant d'afficher des graphiques Smile

Avant de commencer, il est important de comprendre une chose:  un graphique, ce n'est qu'un ensemble de valeur. Après, seule la manière dont ces valeurs sont affichées est susceptible de changer.

Pour représenter un contrôle affichant plusieurs valeurs, avec WPF, vous avez à votre disposition les ItemsControl. Nos allons donc créer un petit contrôle qui hérite d'ItemControl:

public class Graph : ItemsControl

Dès lors, vous n'avez plus grand chose à faire, si ce n'est indiquer la source des données de notre contrôle puis modifier son ItemTemplate afin d'indiquer comment afficher les différents éléments.

Voici un exemple possible d'ItemTemplate, permettant d'afficher un histograme:

<Setter Property="ItemTemplate">

    <Setter.Value>

        <DataTemplate>

            <Rectangle VerticalAlignment="Bottom" Width="25" Height="{Binding}" Fill="Red" Margin="5,0,5,0"/>

        </DataTemplate>

    </Setter.Value>

</Setter>

Pour utiliser ce contrôle, là encore, ce n'est pas  compliqué:

<TabItem Header="Graph">

   <TabItem.DataContext>

      <collections:ArrayList>

         <sys:Int32>25</sys:Int32>

         <sys:Int32>99</sys:Int32>

         <sys:Int32>56</sys:Int32>

      </collections:ArrayList>

   </TabItem.DataContext>

 

   <graph:Graph ItemsSource="{Binding}" />

</TabItem>

Comme vous pouvez le voir, on déclare juste une ArrayList comme étant source des données de notre graph, et à l'affichage, l'ItemTemplate que nous avons définit est bien pris en compte:

image

Bon, c'est sympa tout ca mais on peut pas aller plus loin ? En fait si, on peut faire énormément de choses avec WPF !

Notre contrôle de graph est bien sympathique mais il serait tout de même mieux avec une légende. Nous allons donc implémenter cette légende et plutôt que de fixer une valeur en dur dans le code, nous allons laisser le choix à l'utilisateur....

Pour cela, on va utiliser une DependencyProperty. Pourquoi utiliser une DP et non pas une simple propriété ? En effet, nous n'allons pas binder, animer, transformer, etc... cette propriété (ce que permettent de faire les DP, contrairement aux propriétés simples). Tout simplement parce que l'on ne sait jamais. Qui vous dit que demain, vous n'aurez pas besoin de binder cette propriété à un champ d'une table d'une base de données, etc... Wink

public string Legend

{

   get { return (string)GetValue(LegendProperty); }

   set { SetValue(LegendProperty, value); }

}

 

// Using a DependencyProperty as the backing store for Legend.  This enables animation, styling, binding, etc...

public static readonly DependencyProperty LegendProperty =

            DependencyProperty.Register("Legend", typeof(string), typeof(Graph), new FrameworkPropertyMetadata(string.Empty));

Une fois cette partie terminée, il nous faut modifier le template de notre contrôle, pour lui dire d'afficher cette légende:

<ControlTemplate>

    <Border SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">

        <StackPanel Orientation="Vertical">

            <TextBlock Text="{Binding Path=Legend, RelativeSource={RelativeSource AncestorType={x:Type graph:Graph}}}" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,0,20" />

              <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />

        </StackPanel>

    </Border>

</ControlTemplate>

Beaucoup de code (peut-être compliqué pour certains) pour pas grand chose: on indique que le template de notre Graph sera en fait composé d'une Bordure qui contiendra un StackPanel qui lui-même contiendra une TextBlock (pour la légende) et un ItemPresenter (pour les données). 

Si l'on regarde le résultat, miracle: tout fonctionne Smile

image

Mais ce n'est pas tout ! Un graphique de ce type possède une particularité: les valeurs peuvent-être afficher horizontalement (comme c'est le cas ici) ou bien verticalement. Et dans le 2ème cas, ce n'est pas sur la hauteur des éléments que nous allons devoir binder, mais bien leur largeur. Il va donc nous falloir une propriété qui nous permet de spécifier l'orientation de notre contrôle et suivant la valeur de cette propriété, on utilisera le Template adéquat.

Commençons par la propriété utilisée pour indiquer l'orientation:

public Orientation GraphOrientation

{

   get { return (Orientation)GetValue(GraphOrientationProperty); }

   set { SetValue(GraphOrientationProperty, value); }

}

 

// Using a DependencyProperty as the backing store for GraphOrientation.  This enables animation, styling, binding, etc...

public static readonly DependencyProperty GraphOrientationProperty =

            DependencyProperty.Register("GraphOrientation", typeof(Orientation), typeof(Graph), new FrameworkPropertyMetadata(Orientation.Horizontal));

 Maintenant, comment faire pour indiquer à notre application d'utiliser tel ou tel template, en fonction de la valeur de notre propriété ? Tout simplement en utilisant les Triggers:

<!-- Orientation Vertical -->

<Trigger Property="GraphOrientation" Value="Vertical">

    <Setter Property="ItemTemplate">

       <Setter.Value>

          <DataTemplate>

             <Rectangle HorizontalAlignment="Left" Width="{Binding}" Height="25" Fill="Red" Margin="0,5,0,5"/>

          </DataTemplate>

       </Setter.Value>

    </Setter>

    <Setter Property="ItemsPanel">

       <Setter.Value>

          <ItemsPanelTemplate>

             <StackPanel Orientation="Vertical" />

          </ItemsPanelTemplate>

       </Setter.Value>

    </Setter>

</Trigger>

 

<!-- Orientation Horizontal -->

<Trigger Property="GraphOrientation" Value="Horizontal">

    <Setter Property="ItemTemplate">

       <Setter.Value>

          <DataTemplate>

             <Rectangle VerticalAlignment="Bottom" Width="25" Height="{Binding}" Fill="Red" Margin="5,0,5,0"/>

          </DataTemplate>

       </Setter.Value>

    </Setter>

    <Setter Property="ItemsPanel">

       <Setter.Value>

          <ItemsPanelTemplate>

             <StackPanel Orientation="Horizontal" />

          </ItemsPanelTemplate>

       </Setter.Value>

    </Setter>

</Trigger>

Et le tour est joué: votre contrôle affiche à présent ses éléments de la façon dont vous l'indiquez (ou bien il utilise la valeur par défaut, Orientation.Horizontal):

<graph:Graph ItemsSource="{Binding}" Legend="Various numbers" GraphOrientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" />

 Vous l'aurez compris, si vous souhaitez transformer cet histograme en camembert ou autre, vous n'aurez qu'à modifier le Template et l'ItemTemplate Smile

 

Bon code à vous tous Wink

 

 

A+

 

Technorati tags:
 
Technorati tags:
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: lundi 4 juin 2007 09:23 par Thomas LEBRUN
Classé sous : , ,

Commentaires

H.Greg a dit :

C'est toujours un plaisir de lire des articles ;)

félicitations pour ce que tu fais (on ne le dis pas assez je pense :p)

# juin 4, 2007 18:51

Matthieu MEZIL a dit :

Très intéressant.

Félicitations et merci Thomas.

# juin 5, 2007 01:16
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Silverlight 3 : Communication et multicast par Kévin Gosse le il y a 6 heures et 31 minutes

- [Perso] Découvertes estivales : Linux (Part I) par Le blog de FremyCompany le il y a 9 heures et 12 minutes

- [Refactoring] ReSharper pour Visual Studio 2010 (Preview) par Thomas Jaskula le il y a 23 heures et 48 minutes

- [Refactoring] Analyser vos exceptions avec ReSharper Exceptional par Thomas Jaskula le 07-03-2009, 23:36

- SharePoint 2007 : patterns & practices SharePoint Guidance par Philippe Sentenac [MVP SharePoint] le 07-03-2009, 09:56

- [Visual Studio 2010] Les tests cases c’est bien, mais je vais devoir tout réécrire ? par Etienne Margraff le 07-03-2009, 09:00

- MVP[Gribouillon].AddYear par The Grib's Lair [Sébastien PICAMELOT - MVP SharePoint] le 07-03-2009, 08:45

- Clinique INSIA - Projet de fin d’Etudes (Silverlight 3 MVVM et OutOfBrowser, WCF, TFS) - Part 1 par David REI le 07-02-2009, 23:38

- C’est la crise ? Bah pourquoi cramer du budget pub alors ? par Nix's Blog le 07-02-2009, 15:31

- Soyons MVP ! par TheSaib .NET blog le 07-02-2009, 12:15