Welcome to NetFxFactory Sign in | Join | Help

articles

WPF

Introduction

Windows Presentation Foundation fait partie du dernier .net framework, à savoir le .net framework 3.0. Le framework 3.0 est composé du framework 2.0 auquel s'ajoute WCF (Windows Communication Foundation), WF (Windows Workflow Foundation), WPF (Windows Presentation Foundation) et infocard pour l'authentification des utilisateurs.
Windows Presentation foundation permet de gérer les affichages des fenêtres. Celui-ci est basé sur directx.
En effet jusque maintenant l'affichage des fenêtres sous Windows se faisait en utilisant GDI+, qui est un affichage basé sur les bitmap, un peu comme si votre écran était un énorme BMP que Windows s'amuse à redessiner quand il peut. Maintenant c'est différent, l'affichage se base sur directx et le vectoriel. En fait pour simplifier les choses Directx est la sous couche la plus base de l'api, suit MIL pour Microsoft Intégration Layer, puis WPF. L'avantage du vectoriel c'est que si on agrandit l'affichage, il n'y a pas de pixellisation comme le ferait un bitmap.

 

Les outils pour développer

Sous WPF on a un nouveau langage qui est apparut, le XAML. Ce langage est un sous ensemble du XML. Le but est de séparer la description graphique de la couche métier, un peu comme le fait l'ASP.Net. Le but est de pouvoir déporter la mise en place de l'UI chez le graphiste et la couche métier pour le développeur. Des produits comme la gamme expression sont prévus pour le graphiste, et bien entendu visual studio pour le développeur.
En fait pour avoir une bonne machine de développement pour WPF je vous conseil les choses suivante :
- Un bonne OS serait windows Vista mais XP SP2 ou 2K3 SP1 suffit largement
-Visual studio 2005 ou visual express je conseil C# mais VB.Net est un bon langage aussi
-Windows Vista SDK afin d'avoir les outils nécessaire pour le xaml
-WPF extension pour Visual Studio

Voila vous êtes prés à développer.

Visual Studio 2005 'Orcas'


Prenons un peu en main VS et wpf :

Nous allons créer dans un premier temps un projet avec visual studio :

 

Pour cela nous sélectionnons un nouveau projet, dans le type de projet on sélectionne Visual C# / netframework 3.0. Visual Studio créé un template de projet avec tout ce qui est necessaire pour faire une application WPF. Examinons ce qui a été créé :

 

Premiérement, toutes les références aux DLL nécessaire pour le projet ont été faite, à savoir :
-PresentationCore
-PresentationFramework
-ReachFramework
-WindowsBase

Les deux dll qui commencent par UI sont la pour permettre la compatibilité avec les anciennes applications Win 32.

Ensuite on a un fichier APP.XAML et APP.XAML.CS, c'est deux fichiers sont présents pour la gestion de l'application, c'est ces deux fichiers qui centralisent les événements de l'application et les propriétés de l'application.
Les deux fichiers qui suivent sont les fichiers qu'on utilisera le plus dans l'application car c'est les fichiers qui contrôlent l'affichage de la fenêtre principale. Elle sera la fenêtre principale car on l'aura choisi.

Le XAML

Examinons le fichier XAML :

 

<Window 
x:Class="netfxfactoryApp1.Window1" 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
  Title="netfxfactoryApp1" Height="300" Width="300">
  <Grid> 
  </Grid> 
</Window> 

 

Ce fichier commence par l'élément Window qui caractérise la description d'une fenêtre, sachez qu'il existe plusieurs élément racine pour un fichier XAML, ensuite nous avons l'attribut « x:Class » qui spécifie la classe mère du fichier XAML, c'est la classe dans laquelle sera implémenté toutes les informations concernant la gestion des événements. Puis nous avons la spécification des schémas parent du XAML a savoir http://schemas.microsoft.com/winfx/2006/xaml/presentation et http://schemas.microsoft.com/winfx/2006/xaml puis nous avons le titre de la fenêtre a savoir ici netfxfactoryapp1, la hauteur et la largeur. En fait dans l'élément Window on a toute les propriétés de la fenêtre qui pourront être indiqués.

 

Le Contenu et le contenant

La gestion du contenu est quelque peu différente en WPF par rapport aux applications Win32 classiques, en effet avant on ajoutait des contrôles sur la fenêtre principale, cette fenêtre pouvait contenir des conteneurs qui possédaient d'autres contrôles. On utilisait les containers pour pouvoir regrouper les contrôles entre eux mais le but restait relativement simple. En WPF on utilise des conteneurs qui servent surtout à gérer le layout des contrôles.

Prenons un exemple, je veux mettre en place un formulaire de demande de coordonnées pour une personne dans ce formulaire je veux mettre en place un beau layout, je ne vais utiliser que des textbox et des textblock et une succession de conteneur, voici ce qui est à ma disposition.

Voici dans un premier temps le code XAML :

 

<Window x:Class="netfxfactoryApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="netfxfactoryApp1" Height="300" Width="300">
  <TextBlock>Nom</TextBlock>
</Window> 

Voici le résultat:


Bien maintenant je veux ajouter une texbox a coté de mon textblock pour pouvoir entrer le nom, tout naturelement je pourrais être tenté de mettre ceci dans mon code :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <TextBlock>Nom</TextBlock>
 <TextBox/>
</Window> 

 

Le probléme c'est que maintenant le code ne compile plus et que j'ai une erreur de ce type :

 

En fait Window ne peut avoir qu'un enfant, et si on regarde mon code, ici, il en a deux, un textblock et une textbox. Il faut donc trouver un moyen pour mettre en place un layout chouette pour notre formulaire. Pour cela on va utiliser un container comme enfant de window et ce container pourra avoir plusieurs enfants

Commençons par un conteneur du style StackPanel :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <StackPanel>
  <TextBlock>Nom</TextBlock>
  <TextBox/>
  <TextBlock>Prenom</TextBlock>
  <TextBox/>
  <TextBlock>Adresse</TextBlock>
  <TextBox/>
  <Button>OK</Button>
 </StackPanel>
</Window> 

Cela donne ceci :

 

Comme vous pouvez le voir les contrôles sont mis en pile dans la fenêtre. Surtout qu'on peut changer l'orientation :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <StackPanel Orientation="Horizontal">
 ………
  </StackPanel>
</Window> 

Voici le résultat :

 

Bien entendu c'est beau mais pas vraiment ce que l'on souhaite.

Passons au DockPanel. Le dockpanel se base sur la mise en place des contrôles par rapport aux cotés de la fenêtre et au centre de celle-ci. Commencons par le dockpanel sans informations sur la facon dont les contrôles doivent être placés :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <DockPanel>
  <TextBlock>Nom</TextBlock>
  <TextBox/>
  <TextBlock>Prenom</TextBlock>
  <TextBox/>
  <TextBlock>Adresse</TextBlock>
  <TextBox/>
  <Button>OK</Button>
 </DockPanel>
</Window> 

Ce qui donne ceci :

 

Peu de différence avec le stack panel horizontal hormis le bouton OK qui prend toute la place. En fait en dock panel toute la surface de la fenêtre sera prise. Commencons pas spécifier pour chaque contrôle de se « docker » en haut de la fenêtre sauf pour le bouton qui lui ira en bas :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <DockPanel>
  <TextBlock DockPanel.Dock="Top">Nom</TextBlock>
  <TextBox DockPanel.Dock="Top"/>
  <TextBlock DockPanel.Dock="Top">Prenom</TextBlock>
  <TextBox DockPanel.Dock="Top"/>
  <TextBlock DockPanel.Dock="Top">Adresse</TextBlock>
  <TextBox DockPanel.Dock="Top"/>
  <Button DockPanel.Dock="Bottom">OK</Button>
 </DockPanel>
</Window> 

 

 

 

Jolie on aurait pu faire toute sorte de layout en se basant uniquement sur les cotés de la fenêtre. Par exemple enlevons les Dock properties pour les textbox par défaut elles prendront la valeurs left :

Voici donc le résultat :

 

Continuons notre tours des différents conteneurs, passons maintenant au WrapPanel (alias FlowLayout), la particularité de ce panel est qu'il adapte son contenu à sa dimension en faisant des retours a la ligne automatique, un peu comme le ferais une page HTML. Voyons le code :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300" >
 <WrapPanel>
  <TextBlock>Nom</TextBlock>
  <TextBox Width="100"/>
  <TextBlock>Prenom</TextBlock>
  <TextBox Width="100"/>
  <TextBlock>Adresse</TextBlock>
  <TextBox Width="100"/>
  <Button>OK</Button>
  </WrapPanel>
</Window> 

Ce qui donne:

 

Et si on agrandit:

 

Comme vous pouvez le voir le contenu s'adapte automatiquement. Mais voila le layout me convient toujours pas. Je continu avec le tableau. Il réagit de la même maniére qu'un tableau HTML. Voyons d'abord ce que l'on veut. L'idéal serait un tableau de deux colonnes sur 3 lignes dans les cellules de gauche on mettrait les textblock et dans celle de droite les textbox, le bouton sera sur la derniere ligne et occupera les deux colonnes. Voici le code :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300"> 
 <Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition/>
   <ColumnDefinition/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
  </Grid.RowDefinitions>
  <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
  <TextBox Width="100" Grid.Column="1" Grid.Row ="0"/>
  <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
  <TextBox Width="100" Grid.Column="1" Grid.Row ="1"/>
  <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
  <TextBox Width="100" Grid.Column="1" Grid.Row ="2"/>
  <Button Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</Button>
 </Grid>
</Window> 

Voici le résultat :

 

Bien entendu en spécifiant des tailles et un alignement on peut avoir une plus belle fenêtre encore :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition/>
   <ColumnDefinition/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
  </Grid.RowDefinitions>
  <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0"/>
  <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1"/>
  <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2"/>
  <Button HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</Button>
 </Grid>
</Window> 

 

Le résultat est enfin ce qu'on voulait :

 

 

Comme on a pu le voir dans certains cas les contrôles prennent le maximum de place. Par exemple reprenons le stackpanel :

 

Cela vient de la valeur par defaut de la propriété « «HorizontalAlignment », assez curieux mais en fait par defaut, dans le stackpanel, cette propriété a la valeur strech, ceux qui veut dire qu'on prend toute la place. Si on remplace le code par le suivant :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <StackPanel>
  <TextBlock>Nom</TextBlock>
  <TextBox HorizontalAlignment="Left"/>
  <TextBlock>Prenom</TextBlock>
  <TextBox HorizontalAlignment="Right"/>
  <TextBlock>Adresse</TextBlock>
  <TextBox HorizontalAlignment="Center"/>
  <Button HorizontalAlignment="Stretch">OK</Button>
 </StackPanel>
</Window> 

J'obtiens ceci:

 

Bien entendu je peux ajouter la propriété Width pour agrandir mes textbox. Pour le bouton le fait de spécifier Strech est inutile en pratique puisque c'est la valeur par defaut.
En fait c'est la propriété Content de la class Window qui est mise à jours quand on lui ajoute des enfants. Si on regarde dans le SDK on se rend vite compte que beaucoup d'autre contrôle on cette propriété content. Comme le bouton par exemple. On pourrait donc imaginer quelque chose de farfelue a savoir, c'est le bouton OK qui contiendra le formulaire de demande de coordonées !

Testons donc ceci :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <Button HorizontalAlignment="Stretch">
  <Grid>
   <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition/>
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition/>
    <RowDefinition/>
    <RowDefinition/>
   </Grid.RowDefinitions>
   <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0"/>
   <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1"/>
   <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row="2"/>
   <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</TextBlock>
  </Grid>
 </Button>
</Window> 

 

Si on compile pas d'erreur ?? alors lançons l'application et voila ce qu'on a :

 

Notre bouton contient bien le formulaire. En fait tout ce que vous mettez dans une fenêtre vous pouvez le mettre dans un bouton, a votre imagination de fonctionner maintenant.

Transformation

Tous les contrôles sont rendu en vectoriel et peuvent être transformé, c'est-à-dire tourner, agrandit rétrécit ou déplacer au runtime.

Imaginons, je veux tourner mon bouton de 45° dans le sens horaire. Je vais donc dire a mon fichier XAML de Transformer le layout du bouton en appliquant une Rotation de 45° :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <Button HorizontalAlignment="Stretch">
  <Button.LayoutTransform>
   <RotateTransform Angle="45"/>
  </Button.LayoutTransform>
  <Grid>
   <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition/>
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition/>
    <RowDefinition/>
    <RowDefinition/>
   </Grid.RowDefinitions>
   <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0"/>
   <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1"/>
   <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2"/>
   <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</TextBlock>
  </Grid>
 </Button>
</Window> 

 

 

Assez bluffant non.

DataBinding entre contrôles

Une des fonctionnalités les plus « Sexy » de WPF est la possibilité de faire du databinding entre les différents contrôles de l'application. Prenons un exemple, j'ai envie de tourner mon bouton de quelque degrées comme je l'ai fait au dessus mais en m'aidant d'un slider. Ca serait mieux l'utilisateur pourrait controler la rotation au runtime.
Ajoutons donc le slider au layout de ma fenêtre :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <StackPanel>
  <Slider Minimum="0" Maximum="360"/>
  <Button HorizontalAlignment="Stretch">
   <Button.LayoutTransform>
   <RotateTransform Angle="45"/>
  </Button.LayoutTransform>
  <Grid>
   <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition/>
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition/>
    <RowDefinition/>
    <RowDefinition/>
   </Grid.RowDefinitions>
   <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0"/>
   <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1"/>
   <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
   <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2"/>
   <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</TextBlock>
  </Grid>
 </Button>
</StackPanel>
</Window> 

 

Comme vous le savez déjà la fenetre ne peut avoir qu'un enfant j'ai donc mis un stackpanel en plus . Et voici le résultat :

 

Mais pour l'instant rien ne se passe. En fait je dois nommer le slider pour pouvoir lier l'angle de rotation de mon bouton a mon slider, pour eviter un effet de redimensionnement génant j'ai aussi Fixer la taille de mon bouton.

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <StackPanel>
  <Slider Minimum="0" Maximum="360" Name="sldValue"/>
  <Button HorizontalAlignment="Stretch" Width="100">
   <Button.LayoutTransform>
    <RotateTransform Angle="{Binding ElementName=sldValue, Path=Value}"/>
   </Button.LayoutTransform>
   <Grid>
    <Grid.ColumnDefinitions>
     <ColumnDefinition/>
     <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
     <RowDefinition/>
     <RowDefinition/>
     <RowDefinition/>
     <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
    <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0"/>
    <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
    <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1"/>
    <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
    <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2"/>
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</TextBlock>
   </Grid>
  </Button>
 </StackPanel>
</Window> 

Le resultat est plutôt pas mal :

 

 

StoryBoard

Et si mon bouton s'animait tout seul ? Et oui les animations sont possibles en utilisant ce qu'on appel un storyboard. Comme dans l'animation télé.
Pour cela je dois déjà définir quand mon storyboard va démarrer, pour cela j'utilise les Triggers de la classe window :

 

<Window.Triggers>
 <EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions> 

En effet ici j'utilise un RoutedEvent (on verra à quoi cela sert) pour spécifier que je veux travailler sur le chargement de ma fenêtre. Bien ensuite je défini un nom sur la RotateTransform de mon bouton :

 

<RotateTransform x:Name="btnAngle" /> 

Et J'applique mon storyboard qui pointe sur la RotateTransform. Ce qui définit mon storyboard c'est que la valeur de départ sera 0, la finale sera 360 (pour faire un tour complet), que le temps pour faire ce tour sera de 15 seconde, et qu'il faudra le répéter indéfiniment :

 

<Storyboard>
 <DoubleAnimation From="0" To="360" 
    BeginTime="00:00:00" 
    Duration="00:00:15" 
    RepeatBehavior="Forever" 
    Storyboard.TargetName="btnAngle" 
    Storyboard.TargetProperty="Angle"/>
</Storyboard> 

 

Cette animation est basée sur les doubles donc c'est une DoubleAnimation.
Voila le code au complet :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300">
 <Window.Triggers>
  <EventTrigger RoutedEvent="Window.Loaded">
   <EventTrigger.Actions>
    <BeginStoryboard>
     <Storyboard>
      <DoubleAnimation From="0" To="360" 
       BeginTime="00:00:00" 
       Duration="00:00:15" 
       RepeatBehavior="Forever" 
       Storyboard.TargetName="btnAngle" 
       Storyboard.TargetProperty="Angle"/>
      </Storyboard>
     </BeginStoryboard>
    </EventTrigger.Actions>
   </EventTrigger>
  </Window.Triggers>
  <Button Width="100" Height="100">
   <Button.LayoutTransform>
    <RotateTransform x:Name="btnAngle" />
   </Button.LayoutTransform>
   <Grid>
    <Grid.ColumnDefinitions>
     <ColumnDefinition/>
     <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
     <RowDefinition/>
     <RowDefinition/>
     <RowDefinition/>
     <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
    <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0"/>
    <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
    <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1"/>
    <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
    <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2"/>
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2">OK</TextBlock>
   </Grid>
  </Button>
</Window> 

Et voila le résultat :

 

Evidement a moins de faire un gif animé, vous ne risquez pas de voire l'animation donc c'est a vous de la faire ;-) .

Le code Behind

Tout cela est bien mais pour l'instant notre application ne fait absolument rien. Il faudrait peut être commencé à développer quelque chose. Pour cela on va développer les actions en C#. Mais comment le xaml et le C# peuvent se connaitre ??
Déjà dans le XAML on a une indication sur la classe qui est utilisée pour gérer les événements. En Effet l'attribut x :Class permet de donner le nom de la classe qui réceptionne les events. Ensuite la classe dans le programme C# est Partial, ce qui veut dire qu'elle n'est pas compléte et donc un bout est définit autre part. En fait quand on compile le fichier XAML est « transformé » en C# puis fusionner avec sa classe partielle qui est définit dans x :Class et enfin il est transformé en MSIL. Ici on veut définir que le clique sur le bouton générera un événement et affichera une message box avec les différentes informations contenu dans le formulaire. Pour cela on va changer le XAML pour déjà nommer tous les contrôles. On se basera sur le layout grid vus plus haut. Puis on spécifiera une valeur pour la propriéte click du bouton afin de rediriger le click sur l'appel à une fonction.

Voici le code XAML :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300"> 
 <Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition/>
   <ColumnDefinition/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
  </Grid.RowDefinitions>
  <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0" Name="txtNom"/>
  <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1" Name="txtPrenom"/>
  <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2" Name="txtAdresse"/>
  <Button HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2" Name="btnOK" Click="btnOK_Click">OK</Button>
 </Grid>
</Window> 

 

Et voici le code C# :

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace netfxfactoryApp1 
{ 
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : System.Windows.Window
  {
   public Window1()
   {
    InitializeComponent();
   }
   protected void btnOK_Click(object sender, RoutedEventArgs rea)
   {
    string strMessage = string.Format("Bonjour {0}, {1} vous habitez bien {2}", txtNom.Text, txtPrenom.Text, txtAdresse.Text);
    MessageBox.Show(strMessage);
   }
  }
} 

 

Le résultat est le suivant:

 

Bien il y a peu de différence au premier abord entre la gestion des events sous GDI+ et celle sous WPF. Ben en fait si. La notion de routedevent est très importante, en effet vous avez la possibilité de rediriger tous les RoutedEvent vers une seule gestion événementielle. Ici je vais demander d'afficher le nom du contrôle qui à déclencher l'événement. J'ajoute donc le code suivant a ma fonction C# qui gére l'événement :

protected void btnOK_Click(object sender, RoutedEventArgs rea)
{
 string strMessage = string.Format("Bonjour {0}, {1} vous habitez bien {2}\n C'est le contrôle avec le nom {3} qui a géré l'evenement mais c'est le contrôle {4} qui l'a vraiment déclenché", txtNom.Text, txtPrenom.Text, txtAdresse.Text, ((Control)sender).Name, ((Control)rea.OriginalSource).Name); 

 MessageBox.Show(strMessage);
} 

Bien le résultat actuel n'est pas très probant puisqu'on obtient ceci :

 

En fait comme on n'utilise pas encore les routedevents, on n'a rien de spécial qui se passe. Mais si on souhaite que tous les click sur un bouton soit gérer de maniére centralisée, par exemple par la fenêtre. Cela peut être interressant quand on a des boutons a la fonctionnalité si proche qu'il est dommage d'avoir une répétition de la fonction partout. Par exemple une calculatrice, chaque clique sur un nombre a un business relativement identique. Voici le code que l'on mettrait dans le XAML :

 

<Window x:Class="netfxfactoryApp1.Window1"
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
 Title="netfxfactoryApp1" Height="300" Width="300" Name="MainWnd" 
 Button.Click="btnOK_Click">
 <Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition/>
   <ColumnDefinition/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
   <RowDefinition/>
  </Grid.RowDefinitions>
  <TextBlock Grid.Column="0" Grid.Row ="0">Nom</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="0" Name="txtNom"/>
  <TextBlock Grid.Column="0" Grid.Row ="1">Prenom</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="1" Name="txtPrenom"/>
  <TextBlock Grid.Column="0" Grid.Row ="2">Adresse</TextBlock>
  <TextBox Width="100" VerticalAlignment="Top" Height="20" Grid.Column="1" Grid.Row ="2" Name="txtAdresse"/>
  <Button HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row ="3" Grid.ColumnSpan="2" Name="btnOK">OK</Button>
 </Grid>
</Window> 

J'ai ajouté un nom à la fenêtre pour qu'on puisse voir quelque chose. Voici le résultat :

 

Comme vous pouvez voir le Sender c'est la fenêtre car c'est elle qui gère réellement l'événement mais grâce à la propriété OriginalSource on a le vrai nom du contrôle.

Conclusion

Voici donc la premiére partie de l'introduction au WPF terminée. Dans cette partie on a vue ce qu'était le XAML, comment fonctionner la gestion de contenu et de contenant avec les différents contrôles conteneurs. Puis on a vu comment fonctionne le code behind dans les applications Windows et les routed events. Il nous reste encore plein de chose a voir que j'aborderais ultérieurement.

VANNESTE Xavier

Published Friday, February 02, 2007 12:06 PM by admin
Filed under:

Comments

No Comments
Anonymous comments are disabled

This Blog

Syndication

Powered by Community Server (Personal Edition), by Telligent Systems