Propriétés de dépendance de type collection

Mise à jour : novembre 2007

Cette rubrique propose des conseils et des modèles suggérés pour l'implémentation d'une propriété de dépendance de type collection.

Cette rubrique comprend les sections suivantes.

  • Implémentation d'une propriété de dépendance de type collection
  • Initialisation de la collection au-delà de la valeur par défaut
  • Signalement des modifications des valeurs de liaison des propriétés de collection
  • Rubriques connexes

Implémentation d'une propriété de dépendance de type collection

Dans le cas d'une propriété de dépendance, le modèle d'implémentation que vous suivez consiste généralement à définir un wrapper de propriété CLR, dans lequel cette propriété est soutenue par un identificateur DependencyProperty plutôt que par un champ ou une autre construction. Vous devez suivre ce même modèle lorsque vous implémentez une propriété de type collection. Une propriété de type collection ajoute toutefois une certaine complexité au modèle lorsque le type contenu dans la collection est lui-même une classe dérivée DependencyObject ou Freezable.

Initialisation de la collection au-delà de la valeur par défaut

Lorsque vous créez une propriété de dépendance, vous ne spécifiez pas sa valeur par défaut comme étant la valeur de champ initiale. Vous la spécifiez à l'aide des métadonnées de la propriété de dépendance. Si votre propriété est un type référence, la valeur par défaut spécifiée dans les métadonnées de la propriété de dépendance n'est pas une valeur par défaut par instance, mais bien une valeur par défaut qui s'applique à toutes les instances du type. Par conséquent, vous devez prendre garde de ne pas utiliser la collection statique unique définie par les métadonnées de la propriété de type collection comme valeur par défaut active pour les nouvelles instances créées de votre type. À la place, vous devez vous assurer que vous affectez délibérément à la valeur de collection une collection (instance) unique dans le cadre de votre logique de constructeur de classe. Sinon, vous créerez une classe singleton involontaire.

Prenons l'exemple suivant. La section suivante de l'exemple illustre la définition d'une classe Aquarium. Cette classe définit la propriété de dépendance de type dépendance AquariumObjects, qui utilise le type List<T> générique avec une contrainte de type FrameworkElement. Dans l'appel Register(String, Type, Type, PropertyMetadata) à la propriété de dépendance, les métadonnées stipulent que la valeur par défaut sera une nouvelle List<T> générique.

public class Fish : FrameworkElement { }
public class Aquarium : DependencyObject
{
    private static readonly DependencyPropertyKey AquariumContentsPropertyKey = 
        DependencyProperty.RegisterReadOnly(
          "AquariumContents",
          typeof(List<FrameworkElement>),
          typeof(Aquarium),
          new FrameworkPropertyMetadata(new List<FrameworkElement>())
        );
    public static readonly DependencyProperty AquariumContentsProperty =
        AquariumContentsPropertyKey.DependencyProperty;

    public List<FrameworkElement> AquariumContents
    {
        get { return (List<FrameworkElement>)GetValue(AquariumContentsProperty); }


...


}

Cependant, si vous laissez le code tel qu'illustré, cette valeur par défaut de liste unique sera partagée pour toutes les instances d'Aquarium. Si vous avez exécuté le code de test suivant, qui a pour but de vous montrer comment instancier deux instances d'Aquarium distinctes et ajouter un Fish différent à chacune, vous risquez d'être surpris par le résultat :

Aquarium myAq1 = new Aquarium();
Aquarium myAq2 = new Aquarium();
Fish f1 = new Fish();
Fish f2 = new Fish();
myAq1.AquariumContents.Add(f1);
myAq2.AquariumContents.Add(f2);
MessageBox.Show("aq1 contains " + myAq1.AquariumContents.Count.ToString() + " things");
MessageBox.Show("aq2 contains " + myAq2.AquariumContents.Count.ToString() + " things");

Au lieu d'avoir deux collections contenant chacune une unité, vous avez deux collections de deux unités ! En effet, chaque Aquarium a ajouté son Fish à la collection de valeurs par défaut, car il n'y a eu qu'un seul appel de constructeur dans les métadonnées, lequel a par conséquent été partagé entre toutes les instances. Ce n'est sans doute pas ce que vous vouliez.

Pour résoudre ce problème, vous devez réinitialiser la valeur de la propriété de dépendance de type collection à une instance unique, dans le cadre de l'appel de constructeur de classe. Comme la propriété est une propriété de dépendance en lecture seule, utilisez la méthode SetValue(DependencyPropertyKey, Object) pour la définir, à l'aide du DependencyPropertyKey qui est uniquement accessible dans la classe.

public Aquarium() : base()
{
    SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>()); 
}

Si vous exécutez à nouveau ce même code de test, vous obtiendrez un résultat plus prévisible, où chaque Aquarium prend en charge sa propre collection unique.

Il peut y avoir une légère variation à ce modèle si vous choisissez d'avoir votre propriété de collection en lecture-écriture. Dans ce cas, vous pouvez appeler l'accesseur set public du constructeur pour l'initialisation, ce qui aura toujours pour effet d'appeler la signature non-clé de SetValue(DependencyProperty, Object) dans votre wrapper défini à l'aide d'un identificateur DependencyProperty public.

Signalement des modifications des valeurs de liaison des propriétés de collection

Une propriété de collection qui est elle-même une propriété de dépendance ne signale pas automatiquement les modifications apportées à ses sous-propriétés. Si vous créez des liaisons au sein d'une collection, cela peut empêcher la liaison de signaler les modifications et, de ce fait, invalider certains scénarios de liaison de données. Cependant, si vous utilisez le type collection FreezableCollection<T>, les modifications de sous-propriétés apportées aux éléments contenus dans la collection sont correctement signalées et la liaison donne les résultats escomptés.

Pour activer la liaison de sous-propriétés dans une collection d'objets de dépendance, créez la propriété de collection en tant que type FreezableCollection<T>, avec une contrainte de type pour cette collection à toute classe dérivée DependencyObject.

Voir aussi

Concepts

XAML et classes personnalisées

Vue d'ensemble de la liaison de données

Vue d'ensemble des propriétés de dépendance

Propriétés de dépendance personnalisées

Métadonnées de propriété de dépendance

Référence

FreezableCollection<T>