Soyons à l’écoute les un des autres.

Parfois, il peut être utile de connaitre l’état de certaines propriétés de nos contrôles. Imaginez par exemple qu’en fonction de la visibilité d’un contrôle, vous devez faire un traitement quelconque.

Premier réflexe, utilisation d’un controller (cf. Création & Utilisation d’un Controller). Jusque là rien de bien méchant si ce n’est qu’il faut créer un controller pour chaque type que l’on souhaite surveiller ou avoir un controller de type object qu’il faudra par la suite caster pour récupérer la valeur surveillée.
Avec cette solution, dans tous les cas nous devons avoir :
- une classe controller
- une déclaration du controller en ressource Xaml
- un binding du controller sur la propriété du notre contrôle
- coté C# la récupération de l’instance du controller via les ressources puis l’abonnement à l’évènement de changement de valeur
de propriété.

Deuxième réflexe, utiliser une vue qui implémente l’interface INotifyPropertyChanged et la passer en DataContext de l’objet à observer. Cela peut vite devenir gênant si vous avez déjà une autre vue en Datacontext. Vous devrez alors agréger la vue métier et la vue de gestion de l’interface.

Essayons de prendre un autre angle. Le mot clé des précédentes solutions est : Binding.
Nous pouvons l’effectuer en code behind. Pour cela nous avons besoin du controle et de sa propriété surveillée et lier tout ceci à une propriété de dépendance. La propriété de dépedance doit être de même type que la propriété surveillé ou de type object, mieux être un generic.
Prenons une classe contenant cette propriété de dépendence generic, un évènement levé à la modification de celle-ci, secouons bien fort et nous obtiendrons ceci :

using System.Windows;
using System.Windows.Data;

namespace KL.SLApp.ValueChangedListener
{
/// <summary>
/// DependencyPropertyValueChangedHelper class.
/// You can use this class to be notified when property value changed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class DependencyPropertyValueChangedHelper<T> : FrameworkElement
{
#region "Dependency Properties"
/// <summary>
/// ValueProperty DependencyProperty.
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(T), typeof(DependencyPropertyValueChangedHelper<T>), new PropertyMetadata(default(T), new PropertyChangedCallback(PropertyChangedCallBack)));

/// <summary>
/// Properties the changed call back.
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void PropertyChangedCallBack(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((DependencyPropertyValueChangedHelper<T>)obj).OnPropertyValueChanged(e);
}
#endregion

#region "Constructor"
/// <summary>
/// Initializes a new instance of the <see cref="DependencyPropertyValueChangedHelper<T>"/> class.
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="propertyName">Name of the property.</param>
public DependencyPropertyValueChangedHelper(DependencyObject obj, string propertyName)
{
Binding bind = new Binding(propertyName);
bind.Source = obj;
bind.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(this, ValueProperty, bind);
}
#endregion

#region "Event"
/// <summary>
/// Occurs when [property value changed].
/// </summary>
public event DependencyPropertyChangedEventHandler PropertyValueChanged;
#endregion

#region "Properties"
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public T Value
{
get
{
return (T)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
#endregion

#region "EventsHelper"
/// <summary>
/// Raises the <see cref="E:PropertyValueChanged"/> event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private void OnPropertyValueChanged(DependencyPropertyChangedEventArgs e)
{
if (PropertyValueChanged != null)
{
PropertyValueChanged(this, e);
}
}
#endregion
}
}

L’utilisation de cette classe se fait de la manière suivante :

private DependencyPropertyValueChangedHelper<Visibility> helper;

public MainPage()
{
InitializeComponent();
helper = new DependencyPropertyValueChangedHelper<Visibility>(spViewWithListener, "Visibility");
helper.PropertyValueChanged += new DependencyPropertyChangedEventHandler(Helper_PropertyValueChanged);
}

private void Helper_PropertyValueChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MessageBox.Show(string.Format("Listener event raised. Control visibility has changed !{0}New value = {1}", Environment.NewLine, e.NewValue.ToString()));
}

Et voilà ! Pour surveiller une autre propriété d’un autre contrôle il suffit de changer le type passé en generic et la propriété surveillée.

L’exemple ci-dessous permet de surveiller la propriété Background :

private DependencyPropertyValueChangedHelper<SolidColorBrush> helperColor;

public MainPage()
{
InitializeComponent();
helperColor = new DependencyPropertyValueChangedHelper<SolidColorBrush>(spViewWithListener, "Background");
helperColor.PropertyValueChanged += new DependencyPropertyChangedEventHandler(HelperColor_PropertyValueChanged);
}

private void HelperColor_PropertyValueChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MessageBox.Show("Listener event raised. Control background color has changed.");
}

Pour résumer avec cette solution nous avons besoin :
- de la classe helper contenant la propriété de dépendance generic
- coté C#, l’instantiation du helper puis l’abonnement à l’évènement de changement de la valeur de la propriété.

A cela, plusieurs remarques :
- il faut faire attention à la manière dont on déclare le helper. Dans les exemples ci-dessus il est déclaré en attribut de la classe du contrôle pour que leur cycle de vie soient les même, ou presque. Si nous l’avions déclaré dans le constructeur de notre contrôle de la manière suivante :

public MainPage()
{
InitializeComponent();
DependencyPropertyValueChangedHelper<Visibility> helper = new DependencyPropertyValueChangedHelper<Visibility>(spViewWithListener, "Visibility");
helper.PropertyValueChanged += new DependencyPropertyChangedEventHandler(Helper_PropertyValueChanged);
}

sont cycle de vie commencerait dans le constructeur et se terminerait avec. L’existance de celui-ci dépendrait du garbage collector et nos aurions probablement des effets de bord.
Je le précise, car c’est une erreur que j’ai faite en construisant l’exemple téléchargeable ci-dessous :) .

- Si vous souhaitez surveillez l’activité d’une collection (ajout, suppression, modification d’un élèment), par exemple la propriété Children d’un contrôle Grid. Cette solution ne fonctionnera pas.
Vous ne serez alerté que sur le changement de la référence Children donc sur l’assignation d’une nouvelle collection ou de la valeur null.

Conclusion :

Il n’y a plusieurs façons de répondre à un besoin. Choisir une méthode ou une autre dépend de bien des paramètres (délais, complexité, réutilisation, évolutions, …). La solution présenté ici peut paraître complexe par les mécanismes utilisés : binding, propriété de dépendance, generic, gestion d’évènements, mais elle me semble
plus séduisante par sa simplicité d’utilisation et réutilisabilité (mot presque plus facile à écrire qu’à prononcer) comparée à la solution des controlleurs ou de la vue.

Vous pouvez retrouver un exemple d’utilisation des controlleurs et un exemple d’utilisation du helper dans le projet suivant : [lien sur le projet].

Références :

msdn 1
msdn 2

Silverlight 4 Beta

Ca y est, l’annonce a été faite hier. La nouvelle version de Silverlight estampillée 4 beta est disponible. Première réaction : « Whoua ! ». Seconde réaction : « Qu’est ce qu’elle contient de nouveau ? » et une fois consulté on en reviens à la première réaction ! En effet la liste est longue et comporte beaucoup de nouveautés attendues depuis longtemps : clic droit, gestion de la molette, impression, richtextarea, clipboard, webbrowser, élévation de droits, interop COM, webcam et micro… Certains des contrôles ToolKit ont été également intégrés au framework. Pour pouvoir tester tout ça il vous faudra Visual Studio 2010 et les Silverlight 4 Tools pour Visual Studio 2010.

Retrouvez la liste exhaustive ou presque des nouveautés sur les sites suivant :

Blog de Tim Heuer
Silverlight.Net

Et encore un …

… me direz vous ! Oui, un blog supplémentaire sur la toile !

 Ca fait longtemps que j’y pense et je dois dire que l’envie de tenir un Blog a surpassé ma discrétion démesurée sur la toile. Facebook, blogs, forums, twiter et autres réseaux sociaux ne me connaissent pas et pourtant ils sont la matière qui me rénumèrent.

Travaillant depuis maintenant 8 ans en tant que web developer, j’ai évolué avec et à travers différentes technologies serveur : CGI, AUS, ASP, PHP, ASP.Net. Je dois avouer mon penchant pour l’ASP.Net mais la dernière technologie, qui remporte de loin toute mon estime, est évidement Silverlight.

Les plus tatillons me diront que ce n’est pas une techno orientée serveur mais plutot client. Effectivement, mais son développement n’en reste pas moins très proche de l’esprit ASP.Net. Ceci dit, pourquoi cette technologie merite t’elle toute mon attention ? Parce qu’elle représente tout ce que j’attends d’un developpement web : programmation objet, consolidation de code, comportements identiques sur tous les navigateurs (Firefox, IE, Chrome, …) et environements (Windows, Mac, Linux, …), effets visuels faciles et rapides à mettre en place et j’en passe… tout ceci favorisant un développement rapide et efficace.