Introduit dans la version 3 de Microsoft Expression Blend, les Behaviors sont un système permettant de créer des comportements génériques pour des contrôles. L’un des gros avantages est que ces comportements peuvent être utilisé directement en XAML ou par Blend.
Nous allons partir d’un exemple afin d’étudier comment marche les behaviors. Notre Behavior exemple sera un ResizeBehavior qui va doubler la taille du contrôle auquel il est associé lorsque l’on clique sur le bouton gauche et qui va diviser par deux sa taille quand on clique avec le bouton droit. Le but du jeu étant que ce behavior fonctionne pour un plus grand nombre de contrôles possibles.
- Comment coder un behavior ?
Tout commence avec la classe Behavior<T>. Cette classe est la classe de base des Behaviors. Elle se trouve dans l’assembly System.Windows.Interactivity. Cet assembly ne se trouve pas dans la GAC et pour cause, il s’agit d’un ajout fait par Expression Blend.
Si vous n’avez pas Expression Blend, je vous encourage à télécharger la version Beta pour Silverlight 4 / .NET 4 (http://www.microsoft.com/downloads/details.aspx?FamilyID=6806e466-dd25-482b-a9b3-3f93d2599699&displaylang=en).
Une fois Blend installé, vous devriez trouver cet assembly dans le répertoire :
C:\Program Files\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\Silverlight
C:\Program Files\Microsoft SDKs\Expression\\Blend Preview for .NET 4\Interactivity\Libraries\Silverlight
Pour créer un nouveau behavior, il suffit de créer une classe dérivant de Behavior<T> en spécifiant, à la place du T, le type d’objet que va être appelé à manipuler le Behavior. Dans notre exemple, nous prendrons des contrôles de type FrameworkElement (afin d’avoir accès aux propriétés Width/Height).
public class ResizeBehavior : Behavior<FrameworkElement> { public ResizeBehavior() {} protected override void OnAttached() { base.OnAttached(); //Code } protected override void OnDetaching() { base.OnDetaching(); //Code } }
Comme on peut le voir, la classe ResizeBehavior contient :
- Un constructeur : Pour initialiser les données
- Un méthode OnAttached : Méthode hérité de Behavior<T>. Cette méthode est appelé lorsque le behavior est “attaché” à l’objet. Nous verrons plus loin quand, exactement, à lieu l’attachement. Pour le moment, on admettra que l’attachement se fait lors de la création du contrôle sur lequel va intervenir le behavior.
- Une méthode OnDetaching : Méthode hérité de Behavior<T>. Cette méthode est appelé lorsque le behavior est “attaché” à l’objet. Le détachement est réalisé lorsque le contrôle sur lequel s’applique le behavior est détruit (Nettoyage de la mémoire, désabonnement des événements …).
On remarque aussi un appel aux méthodes OnAttached/OnDetaching parentes. Ces appels sont nécessaires au bon fonctionnement du behavior et doivent toujours être présente au début des méthodes correspondantes.
Comme il a été expliqué au début de cet article, un behavior est un comportement associé à un contrôle. Ce contrôle est disponible au sein du behavior sous la forme de la propriété AssociatedObject (défini dans Behavior<T>.). Vous pouvez ainsi manipuler l’objet comme si vous étiez dans le code behind du fichier XAML dont il dépend. Ainsi nous allons ajouter les évènements MouseLeftButtonDown/MouseRightButtonDown sur l’objet associé afin de modifier la taille de l’objet associé.
public class ResizeBehavior : Behavior<FrameworkElement> { public ResizeBehavior() {} protected override void OnAttached() { base.OnAttached(); //Code this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown); this.AssociatedObject.MouseRightButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown); } protected override void OnDetaching() { base.OnDetaching(); //Code this.AssociatedObject.MouseLeftButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown); this.AssociatedObject.MouseRightButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown); } void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { this.AssociatedObject.Width *= 2; this.AssociatedObject.Height *= 2; } void AssociatedObject_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { this.AssociatedObject.Width /= 2; this.AssociatedObject.Height /= 2; } }
- C’est bien beau tout ça, mais comment on utilise ça en XAML ?
Coté XAML, il faut tout d’abord ajouter deux références. Une pour avoir accès au namespace System.Windows.Interactivity et une pour avoir accès au behavior.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:BehaviorDemo" >
Ensuite, on ajoute un contrôle de type FrameworkElement (un Rectangle par exemple).
<Rectangle Fill="Red" Width="200" Height="100"> <i:Interaction.Behaviors> <local:ResizeBehavior /> </i:Interaction.Behaviors> </Rectangle>
Le namespace System.Windows.Interactivity ajoute une classe Interaction permettant d’associer des Behaviors (mais aussi des Triggers / Actions… qui seront l’objet d’autres posts de blogs :)) .
Lors de l’exécution de l’application, Silverlight va créer l’objet Rectangle puis le ResizeBehavior. Une fois les deux objets créés, la méthode OnAttached de Behavior<T> est appelée. Lors de la destruction de l’objet Rectangle, c’est la méthode OnDetaching qui est appelée.
- Moi j’aime pas le XAML. Je veux du C# …
Le code suivant permet d’associer un behavior à un contrôle.
Rectangle b = new Rectangle(); b.Fill = new SolidColorBrush(Colors.Purple); b.Width = 400; b.Height = 200; Interaction.GetBehaviors(b).Add(new ResizeBehavior()); this.LayoutRoot.Children.Add(b);
- Pourquoi utiliser des behaviors ?
L’intérêt des behavior est multiple :
- Réutilisation du code : Le code de comportement n’est pas lié à un contrôle défini mais à une famille de contrôle. Cela évite de réécrire le code à chaque fois.
- Instanciation directement dans le XAML (donc utilisable dans Blend par un designer)
- Intégration dans Blend : Blend 3/4 gère les behaviors nativement et permet d’ajouter facilement ses propres behaviors.
- Saupoudrons avec un peu d’animations …
On modifie le ResizeBehavior afin d’avoir une animation un peu plus sympa.
public class ResizeBehavior : Behavior<FrameworkElement> { public ResizeBehavior() {} protected override void OnAttached() { base.OnAttached(); //Code this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown); this.AssociatedObject.MouseRightButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown); } protected override void OnDetaching() { base.OnDetaching(); //Code this.AssociatedObject.MouseLeftButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown); this.AssociatedObject.MouseRightButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown); } void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Storyboard sb = new Storyboard(); DoubleAnimation da = new DoubleAnimation() { From = this.AssociatedObject.Width, To = this.AssociatedObject.Width * 1.5, Duration = new Duration(new TimeSpan(0, 0, 1)), EasingFunction = new ElasticEase() }; Storyboard.SetTarget(da, this.AssociatedObject); Storyboard.SetTargetProperty(da, new PropertyPath("(Width)")); sb.Children.Add(da); DoubleAnimation da2 = new DoubleAnimation() { From = this.AssociatedObject.Height, To = this.AssociatedObject.Height * 1.5, Duration = new Duration(new TimeSpan(0, 0, 1)), EasingFunction = new ElasticEase() }; Storyboard.SetTarget(da2, this.AssociatedObject); Storyboard.SetTargetProperty(da2, new PropertyPath("(Height)")); sb.Children.Add(da2); sb.Begin(); } void AssociatedObject_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { Storyboard sb = new Storyboard(); DoubleAnimation da = new DoubleAnimation() { From = this.AssociatedObject.Width, To = this.AssociatedObject.Width / 1.5, Duration = new Duration(new TimeSpan(0, 0, 1)), EasingFunction = new ElasticEase() }; Storyboard.SetTarget(da, this.AssociatedObject); Storyboard.SetTargetProperty(da, new PropertyPath("(Width)")); sb.Children.Add(da); DoubleAnimation da2 = new DoubleAnimation() { From = this.AssociatedObject.Height, To = this.AssociatedObject.Height / 1.5, Duration = new Duration(new TimeSpan(0, 0, 1)), EasingFunction = new ElasticEase() }; Storyboard.SetTarget(da2, this.AssociatedObject); Storyboard.SetTargetProperty(da2, new PropertyPath("(Height)")); sb.Children.Add(da2); sb.Begin(); } }
Code XAML Associé :
<UserControl x:Class="BehaviorDemo.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:BehaviorDemo" > <StackPanel x:Name="LayoutRoot" Background="White"> <Rectangle Fill="Red" Width="200" Height="100"> <i:Interaction.Behaviors> <local:ResizeBehavior /> </i:Interaction.Behaviors> </Rectangle> <Ellipse Fill="Yellow" Width="200" Height="100"> <i:Interaction.Behaviors> <local:ResizeBehavior /> </i:Interaction.Behaviors> </Ellipse> </StackPanel> </UserControl>
- … et voyons le résultat :
Voici des copies d”écrans du résultats :
Gauche : Après chargement / Droite : Après avoir cliqué avec le bouton gauche sur le rectangle rouge et l’ellipse jaune.
Ce post n’était qu’une introduction sur les behaviors. Je vais surement continuer sur ce sujet dans les prochains jours (notamment sur les behaviors/triggers et leurs applications plus business).
Si vous avez des questions et/ou remarques et/ou sujet de futur post, n’hésitez pas à poster un commentaire …
Remarque : Afin d’anticiper certains commentaires, je précise qu’il aurait été possible de créer l’animation dans le OnAttached et non en créer une nouvelle à chaque click :). Je referais peut être l’exemple afin de le rendre plus propre. Toutefois, le but de cet article est de montrer le principe des behaviors.