Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Un contrôle WPF pour visionner ses photos de vacances (en quelques minutes)

Avant, lorsqu’on utilisait un ListBox, on avait assez peu de marge de manoeuvre. On ne pouvait mettre que du texte à l’intérieur, et la personnalisation du contrôle était très limitée.

Avec WPF, tout a changé. Un contrôle n’est plus qu'un ensemble de fonctionnalités. Par exemple, avec WPF, un ListBox, c’est un contrôle qui propose un choix parmi plusieurs éléments. Après, libre à vous de le faire horizontal, vertical, rond, vert, bleu... etc.

Je vais donner un petit exemple de ça en créant un petit contrôle qui affiche une liste d'image qui s'agrandissent lorsqu'on passe la souris dessus. J'ai procédé de la façon suivante : je suis parti d'un ItemsControl, qui est un contrôle très général. Il a pour but principal d'afficher une liste d'éléments, sans permettre la selection d'un élément (pour celà il faut se pencher vers le Selector ou ses dérivés comme le ListBox).

WPF

Pour faire ce contrôle, j'ai utilisé Expression Blend, qui a mon avis est un logiciel indispensable à tout développeur (qui travaille avec WPF). Avec Blend, j'ai fait ce contrôle en 20 minutes, à peu près, alors qu'il m'aurait fallu plusieurs heures si j'avais codé "à la main".

Le contrôle sans Template    Contrôle WPF

A gauche, le ItemsControl auquel on n'applique aucun Template. A droite, celui que nous aurons à la fin de l'article.

Lorsqu'on personnalise un contrôle, la marche à suivre est plus ou moins toujours la même.

Définir un ControlTemplate

Le ControlTemplate indique comment sera affiché le contrôle. Si vous n'en indiquez pas, le template par défaut de Windows sera utilisé.

Sous Blend, pour pouvoir éditer le template, faites clic droit sur le contrôle, puis "Edit Control Parts (Template)" / "Edit a copy...".

Le ControlTemplate sera tout simplement constitué d'un ScrollViewer qui permet de défiler, contenant lui même un StackPanel. J'ai mis la propriété IsItemsHost à true pour ce StackPanel. Cela indique que les éléments fils de ce contrôle seront affichés à l'intérieur de ce StackPanel. L'autre solution aurait été d'utiliser un un ItemsPresenter.

Voici le code XAML correspondant :

<ControlTemplate TargetType="{x:Type ItemsControl}" x:Key="ItemsControlTemplate">
  <Border Background="{TemplateBinding Background}"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}"
    Padding="{TemplateBinding Padding}"
    SnapsToDevicePixels="true">
    <ScrollViewer Width="Auto" Background="{TemplateBinding Background}">
      <StackPanel IsItemsHost="true" Orientation="Vertical"/>
    </ScrollViewer>
  </Border>
</ControlTemplate>

Vous avez peut être remarqué les TemplateBinding. Lorque vous créerez ce contrôle dans une fenêtre par exemple, vous pourrez définir une couleur Backgroud. Cette couleur sera alors utilisée à chaque endroit où vous aurez spécifié {TemplateBinding Background} dans le ControlTemplate. Cela permet de rendre le template plus générique.

Définir un DataTemplate

Le DataTemplate est la 2e étape. Elle consiste à définir de quelle façon seront affichés les différents éléments enfants du contrôle.

<DataTemplate x:Key="ImageDataTemplate">
  <Border Margin="4,4,4,4">
    <Image Width="200" Height="150" Source="{Binding Mode=OneWay}" Opacity="0.5" Cursor="Hand" x:Name="image"/>
  </Border>
</DataTemplate>

Dans notre cas, c'est tout simplement une image à l'intérieur d'une Border. Le DataTemplate sera appliqué pour chaque enfant du contrôle (donc pour chaque image). Et pour chaque élément, le DataContext à l'intérieur du DataTemplate est l'objet qui est en train d'être affiché. Dans notre cas les éléments fils de l'ItemsControl seront des strings qui représentent les sources des images à afficher.

Il faut également définir des animations et des triggers pour que la taille de la photo et l'opacité change au passage de la souris. Ce code est assez rébarbatif à écrire à la main. Avec Blend, il faut à peine 1 minute pour l'écrire.

<DataTemplate x:Key="ImageDataTemplate">

  <DataTemplate.Resources>
    <Storyboard x:Key="Enflate">
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(FrameworkElement.Width)">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="400"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(FrameworkElement.Height)">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="300"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="Deflate">
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(FrameworkElement.Width)">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="200"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(FrameworkElement.Height)">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="150"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Opacity)">
        <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.5"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    </DataTemplate.Resources>

    <Border Margin="4,4,4,4">
      <Image Width="200" Height="150" Source="{Binding Mode=OneWay}" Opacity="0.5" Cursor="Hand" x:Name="image"/>
    </Border>

    <DataTemplate.Triggers>
    <EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="image">
      <BeginStoryboard Storyboard="{StaticResource Enflate}"/>
    </EventTrigger>
    <EventTrigger RoutedEvent="Mouse.MouseLeave">
      <BeginStoryboard x:Name="Deflate_BeginStoryboard" Storyboard="{StaticResource Deflate}"/>
    </EventTrigger>
  </DataTemplate.Triggers>

</DataTemplate>

Définir un Style

Un style est un "package" qui indique des valeurs prédéfinies pour un certain nombre de propriétés. Dans notre cas, nous souhaiterions que la propriété Template du controle prenne comme valeur le ControlTemplate que nous avons précedemment défini et que la propriété ItemTemplate prenne comme valeur le DataTemplate que nous avons défini en 2e partie.

Cela donne donc la chose suivante :

<Style
  TargetType="{x:Type ItemsControl}" x:Key="ItemsControlStyle">
  <Setter Property="Template" Value="{DynamicResource ItemsControlTemplate}"/>
  <Setter Property="ItemTemplate" Value="{DynamicResource ImageDataTemplate}"/>
</Style>

Et voilà...

Il ne reste plus qu'à créer le contrôle dans une fenêtre en lui spécifiant le style que nous venons de faire :

<ItemsControl Margin="8,8,8,8" Style="{DynamicResource ItemsControlStyle}" ItemsSource="{DynamicResource Liste}">
  <ItemsControl.Background>
    <LinearGradientBrush EndPoint="0.945,0.967" StartPoint="0.036,0.05">
      <GradientStop Color="#FFCAE7F4" Offset="0"/>
      <GradientStop Color="#FFFFFFFF" Offset="1"/>
    </LinearGradientBrush>
  </ItemsControl.Background>
</ItemsControl>

Ici j'ai en plus ajouté un dégradé pour le Background. Vous pouvez remarquer que comme prévu dans le ControlTemplate, ce dégradé s'affiche bien comme couleur de fond du ScrollViewer (souvenez vous le {TemplateBinding Background}).

Pour ce qui est des enfants de l'ItemsControl, ils sont liés à une collection en ressource (grâce à la propriété ItemsSource) :

<Window.Resources>
  <col:ArrayList x:Key="Liste">
    <s:String>F:\Images\Imagine Cup India\CIMG0823.JPG</s:String>
    <s:String>F:\Images\Imagine Cup India\CIMG0825.JPG</s:String>
    <s:String>F:\Images\Imagine Cup India\CIMG0826.JPG</s:String>
    <s:String>F:\Images\Imagine Cup India\CIMG0827.JPG</s:String>
    <s:String>F:\Images\Imagine Cup India\CIMG0828.JPG</s:String>
    <s:String>F:\Images\Imagine Cup India\CIMG0831.JPG</s:String> 
  </col:ArrayList>     
</Window.Resources>

Pour que cela fonctionne, n'oubiez pas de mapper le préfixe s au namespace CLR System et col au namespace System.Collections.

Publié mardi 16 janvier 2007 22:36 par RaptorXP
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 :

Commentaires

Pas de commentaires
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Changer l’adresse d’une ferme Office Web Apps associée à SharePoint par Blog de Jérémy Jeanson le 09-01-2014, 22:21

- Une ferme #SharePoint 2013 dans @Azure en quelques clics (1ère partie) ! par Le blog de Patrick [MVP SharePoint] le 08-28-2014, 18:52

- SharePoint 2013: Préparation de la migration - Création des site Templates dans 2010 et 2013 par Blog Technique de Romelard Fabrice le 08-20-2014, 16:31

- [ #Yammer ] How to change interface language ? Comment changer la langue de l’interface ? par Le blog de Patrick [MVP SharePoint] le 08-20-2014, 14:21

- Onedrive Sync Engine Host : CPU à 100% par Le petit blog de Pierre / Pierre's little blog le 08-06-2014, 22:22

- SharePoint : Bug sur la gestion des permissions et la synchronisation Office par Blog Technique de Romelard Fabrice le 07-10-2014, 11:35

- SharePoint 2007 : La gestion des permissions pour les Workflows par Blog Technique de Romelard Fabrice le 07-08-2014, 11:27

- TypeMock: mock everything! par Fathi Bellahcene le 07-07-2014, 17:06

- Coding is like Read par Aurélien GALTIER le 07-01-2014, 15:30

- Mes vidéos autour des nouveautés VS 2013 par Fathi Bellahcene le 06-30-2014, 20:52