Dependency Property : Trucs et astuces

by Nicolas Calvi 19. octobre 2010 12:06
Lors des développements en WPF on a souvent besoin d'écrire des propriétés de dépendances pour nos objets. En dehors de la déclaration simple de notre propriété, il y a quelques éléments qui méritent d'être connue. Dans l'exemple qui va suivre, je vais écrire une propriété de dépendance appelée "MaProp", qui sera un Int32 et qui ne peut prendre des valeurs que de 1 à 100.
  
1 - Déclaration de la propriété
 
Il faut bien évidement déclarer sa propriété, dans Visual Studio il y un a snipet pour ça : propdp.
 
public static readonly DependencyProperty MaPropProperty = DependencyProperty.Register("MaProp", typeof(int), typeof(MaClasse), new UIPropertyMetadata(1));
 
Dans les conventions de nommage, on ajoute toujours "Property" sur l'a déclaration statique, ensuite pour initialiser celle-ci, on peut donner une valeur par défaut dans le dernier paramètre, ici 1.
 
2 - Déclaration de l'accesseur
 
Il faut maintenant créer l'accesseur pour cette propriété, ici rien de bien difficile, on doit simplement utiliser le GetValue(DependencyProperty) et le SetValue(DependencyProperty, object). Ces deux fonctions ne font que récupérer ou inscrire des informations dans le dictionnaire de ressource de l'objet, c'est dans celui-ci que les valeurs des propriétés de dépendance sont stockées.
public int MaProp
{
  get
  {
    return ((int)this.GetValue(MaClasse.MaProp));
  }
  set
  {
    this.SetValue(MaProp.MaProp, value);
  }
}
Il est très fortement recommander de ne mettre aucun autre code que celui-ci sur sa propriété. Pour exécuter du code à son changement ou contrôler la valeur, ce sont les deux points suivants.
  
3 - Faire un traitement après modification
 
Pour exécuter du code après un changement de valeur, il est possible de rajouter un Callback dans le constructeur du paramètre UIPropertyMetadata() dans la déclaration statique de la propriété. Ce Callback est de type PropertyChangedCallback. Voici un exemple de déclaration pour cette méthode.
public class MaClasse 
{   
  // Déclaration de la propriété   
  public static readonly DependencyProperty MaPropProperty = DependencyProperty.Register("MaProp", typeof(int), typeof(MaClasse), new UIPropertyMetadata(1, new PropertyChangedCallback(MaClasse.OnMaPropChanged)));    

  // Déclaration de la fonction statique de Callback   
  private static void OnMaPropChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)   
  {    
    // Appel a la fonction interne de modification     
    ((MaClasse)sender).OnMaPropChanged(e.OldValue, e.NewValue);  
  }    
  
  // Déclaration de la fonction interne de modification  
  protected virtual void OnMaPropChanged(int old, int new)   
  {    
     // Votre code ici   
  } 
}
Il est recommandé de déclarer la fonction statique de Callback en privée et la fonction de modification interne en protégée virtuel, ceci permet si on hérite de votre classe, de pouvoir intercepter le changement de valeur de la propriété.
 
4 - Tester la valeur avant modification
Dans notre exemple, on veut pouvoir borner la valeur entre 1 et 100, le premier mécanisme est déjà de l'initialiser dans cet interval, mais il est possible aussi avant l'affectation, de tester la valeur, ceci se passe comme pour le point 3, une fonction de Callback nous permet de la tester, elle s'appelle CoerceValueCallback(). Elle fonctionne comme le PropertyChangedCallback, voici un exemple pour cette méthode
 
public class MaClasse 
{       
  // Déclaration de la propriété       
  public static readonly DependencyProperty MaPropProperty = DependencyProperty.Register("MaProp", typeof(int), typeof(MaClasse), new UIPropertyMetadata(1, new PropertyChangedCallback(MaClasse.OnMaPropChanged), new CoerceValueCallback(MaClasse.CoerceMaProp)));      

  // Déclaration de la fonction statique de Callback  
  private static void OnMaPropChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)  
  {    
    // Appel a la fonction interne de modification    
    ((MaClasse)sender).OnMaPropChanged(e.OldValue, e.NewValue);  
  }    

  // Déclaration de la fonction statique de coerce  
  private static object CoerceMaProp(DependencyObject d, object baseValue)  
  {    
    return(((MaClasse)sender).CoerceMaProp((int)baseValue));  
  }    

  // Déclaration de la fonction interne de modification  
  protected virtual void OnMaPropChanged(int old, int new)  
  {    
    // Votre code ici  
  }    

  // Déclaration de la fonction interne de coerce  
  protected virtual object CoerceMaProp(int value)  
  {    
    // On teste la valeur    
    if (value < 1)      
      return(1);    
    else if (value > 100)      
      return(100);          

    return(value);  
  } 
}
On peut ainsi tester la valeur dans le coerce et la remettre dans un interval souhaité avant son affectation, ce qui déclenchera ensuite le PropertyChangedCallback avec la bonne valeur.