Share via


Propriétés de dépendance de type collection (WPF .NET)

Cet article fournit des conseils et des modèles suggérés pour implémenter une propriété de dépendance qui est un type de collection.

Important

La documentation du Guide du bureau pour .NET 7 et .NET 6 est en cours de construction.

Prérequis

L’article suppose une connaissance de base des propriétés de dépendance et que vous avez lu la vue d’ensemble des propriétés de dépendance. Pour suivre les exemples de cet article, il vous aide à connaître le langage XAML (Extensible Application Markup Language) et à savoir comment écrire des applications WPF.

Implémenter une propriété de dépendance de type collection

En général, le modèle d’implémentation d’une propriété de dépendance est un wrapper de propriété CLR soutenu par un identificateur au lieu d’un DependencyProperty champ ou d’une autre construction. Vous pouvez suivre le même modèle lorsque vous implémentez une propriété de dépendance de type collection. Le modèle est plus complexe si le type d’élément de collection est une DependencyObject classe dérivée ou une Freezable classe dérivée.

Initialiser la collection

Lorsque vous créez une propriété de dépendance, vous spécifiez généralement la valeur par défaut par le biais de métadonnées de propriété de dépendance au lieu de spécifier une valeur de propriété initiale. Toutefois, si votre valeur de propriété est un type référence, la valeur par défaut doit être définie dans le constructeur de la classe qui inscrit la propriété de dépendance. Les métadonnées de propriété de dépendance ne doivent pas inclure de valeur de type référence par défaut, car cette valeur sera affectée à toutes les instances de la classe, créant une classe singleton.

L’exemple suivant déclare une Aquarium classe qui contient une collection d’éléments FrameworkElement dans un générique List<T>. Une valeur de collection par défaut n’est pas incluse dans la PropertyMetadata méthode passée RegisterReadOnly(String, Type, Type, PropertyMetadata) , et au lieu de cela, le constructeur de classe est utilisé pour définir la valeur de collection par défaut sur un nouveau générique List.

public class Aquarium : DependencyObject
{
    // Register a dependency property with the specified property name,
    // property type, owner type, and property metadata.
    private static readonly DependencyPropertyKey s_aquariumContentsPropertyKey =
        DependencyProperty.RegisterReadOnly(
          name: "AquariumContents",
          propertyType: typeof(List<FrameworkElement>),
          ownerType: typeof(Aquarium),
          typeMetadata: new FrameworkPropertyMetadata()
          //typeMetadata: new FrameworkPropertyMetadata(new List<FrameworkElement>())
        );

    // Set the default collection value in a class constructor.
    public Aquarium() => SetValue(s_aquariumContentsPropertyKey, new List<FrameworkElement>());

    // Declare a public get accessor.
    public List<FrameworkElement> AquariumContents =>
        (List<FrameworkElement>)GetValue(s_aquariumContentsPropertyKey.DependencyProperty);
}

public class Fish : FrameworkElement { }
Public Class Aquarium
    Inherits DependencyObject

    ' Register a dependency property with the specified property name,
    ' property type, owner type, and property metadata.
    Private Shared ReadOnly s_aquariumContentsPropertyKey As DependencyPropertyKey =
        DependencyProperty.RegisterReadOnly(
            name:="AquariumContents",
            propertyType:=GetType(List(Of FrameworkElement)),
            ownerType:=GetType(Aquarium),
            typeMetadata:=New FrameworkPropertyMetadata())
            'typeMetadata:=New FrameworkPropertyMetadata(New List(Of FrameworkElement)))

    ' Set the default collection value in a class constructor.
    Public Sub New()
        SetValue(s_aquariumContentsPropertyKey, New List(Of FrameworkElement)())
    End Sub

    ' Declare a public get accessor.
    Public ReadOnly Property AquariumContents As List(Of FrameworkElement)
        Get
            Return CType(GetValue(s_aquariumContentsPropertyKey.DependencyProperty), List(Of FrameworkElement))
        End Get
    End Property
End Class

Public Class Fish
    Inherits FrameworkElement
End Class

Le code de test suivant instancie deux instances distinctes Aquarium et ajoute un élément différent Fish à chaque collection. Si vous exécutez le code, vous verrez que chaque Aquarium instance a un élément de collection unique, comme prévu.

private void InitializeAquariums(object sender, RoutedEventArgs e)
{
    Aquarium aquarium1 = new();
    Aquarium aquarium2 = new();
    aquarium1.AquariumContents.Add(new Fish());
    aquarium2.AquariumContents.Add(new Fish());
    MessageBox.Show(
        $"aquarium1 contains {aquarium1.AquariumContents.Count} fish\r\n" +
        $"aquarium2 contains {aquarium2.AquariumContents.Count} fish");
}
Private Sub InitializeAquariums(sender As Object, e As RoutedEventArgs)
    Dim aquarium1 As New Aquarium()
    Dim aquarium2 As New Aquarium()
    aquarium1.AquariumContents.Add(New Fish())
    aquarium2.AquariumContents.Add(New Fish())
    MessageBox.Show($"aquarium1 contains {aquarium1.AquariumContents.Count} fish{Environment.NewLine}" +
                    $"aquarium2 contains {aquarium2.AquariumContents.Count} fish")
End Sub

Toutefois, si vous commentez le constructeur de classe et transmettez la valeur de collection par défaut à PropertyMetadata la RegisterReadOnly(String, Type, Type, PropertyMetadata) méthode, vous verrez que chaque Aquarium instance obtient deux éléments de collection ! Cela est dû au fait que les deux Fish instances sont ajoutées à la même liste, qui est partagée par toutes les instances de la classe Aquarium. Par conséquent, lorsque l’intention est que chaque instance d’objet ait sa propre liste, la valeur par défaut doit être définie dans le constructeur de classe.

Initialiser une collection en lecture-écriture

L’exemple suivant déclare une propriété de dépendance de type collection en lecture-écriture dans la Aquarium classe, à l’aide des méthodes Register(String, Type, Type) de signature non clés et SetValue(DependencyProperty, Object).

public class Aquarium : DependencyObject
{
    // Register a dependency property with the specified property name,
    // property type, and owner type. Store the dependency property
    // identifier as a public static readonly member of the class.
    public static readonly DependencyProperty AquariumContentsProperty =
        DependencyProperty.Register(
          name: "AquariumContents",
          propertyType: typeof(List<FrameworkElement>),
          ownerType: typeof(Aquarium)
        );

    // Set the default collection value in a class constructor.
    public Aquarium() => SetValue(AquariumContentsProperty, new List<FrameworkElement>());

    // Declare public get and set accessors.
    public List<FrameworkElement> AquariumContents
    {
        get => (List<FrameworkElement>)GetValue(AquariumContentsProperty);
        set => SetValue(AquariumContentsProperty, value);
    }
}
Public Class Aquarium
    Inherits DependencyObject

    ' Register a dependency property with the specified property name,
    ' property type, and owner type. Store the dependency property
    ' identifier as a static member of the class.
    Public Shared ReadOnly AquariumContentsProperty As DependencyProperty =
        DependencyProperty.Register(
            name:="AquariumContents",
            propertyType:=GetType(List(Of FrameworkElement)),
            ownerType:=GetType(Aquarium))

    ' Set the default collection value in a class constructor.
    Public Sub New()
        SetValue(AquariumContentsProperty, New List(Of FrameworkElement)())
    End Sub

    ' Declare public get and set accessors.
    Public Property AquariumContents As List(Of FrameworkElement)
        Get
            Return CType(GetValue(AquariumContentsProperty), List(Of FrameworkElement))
        End Get
        Set
            SetValue(AquariumContentsProperty, Value)
        End Set
    End Property
End Class

Propriétés de dépendance FreezableCollection

Une propriété de dépendance de type collection ne signale pas automatiquement les modifications dans ses sous-propriétés. Par conséquent, si vous effectuez une liaison à une collection, la liaison peut ne pas signaler de modifications, invalidant certains scénarios de liaison de données. Toutefois, si vous utilisez FreezableCollection<T> pour le type de propriété de dépendance, les modifications apportées aux propriétés des éléments de collection sont correctement signalées et la liaison fonctionne comme prévu.

Pour activer la liaison de sous-propriété dans une collection d’objets de dépendance, utilisez le type de collection, avec une contrainte de type FreezableCollectionde toute DependencyObject classe dérivée.

L’exemple suivant déclare une Aquarium classe qui contient une FreezableCollection contrainte de type .FrameworkElement Une valeur de collection par défaut n’est pas incluse dans la PropertyMetadataRegisterReadOnly(String, Type, Type, PropertyMetadata) méthode passée, et au lieu de cela, le constructeur de classe est utilisé pour définir la valeur de collection par défaut sur un nouveau FreezableCollection.

public class Aquarium : DependencyObject
{
    // Register a dependency property with the specified property name,
    // property type, and owner type.
    private static readonly DependencyPropertyKey s_aquariumContentsPropertyKey =
        DependencyProperty.RegisterReadOnly(
          name: "AquariumContents",
          propertyType: typeof(FreezableCollection<FrameworkElement>),
          ownerType: typeof(Aquarium),
          typeMetadata: new FrameworkPropertyMetadata()
        );

    // Store the dependency property identifier as a static member of the class.
    public static readonly DependencyProperty AquariumContentsProperty =
        s_aquariumContentsPropertyKey.DependencyProperty;

    // Set the default collection value in a class constructor.
    public Aquarium() => SetValue(s_aquariumContentsPropertyKey, new FreezableCollection<FrameworkElement>());

    // Declare a public get accessor.
    public FreezableCollection<FrameworkElement> AquariumContents =>
        (FreezableCollection<FrameworkElement>)GetValue(AquariumContentsProperty);
}
Public Class Aquarium
    Inherits DependencyObject

    ' Register a dependency property with the specified property name,
    ' property type, and owner type.
    Private Shared ReadOnly s_aquariumContentsPropertyKey As DependencyPropertyKey =
        DependencyProperty.RegisterReadOnly(
            name:="AquariumContents",
            propertyType:=GetType(FreezableCollection(Of FrameworkElement)),
            ownerType:=GetType(Aquarium),
            typeMetadata:=New FrameworkPropertyMetadata())

    ' Set the default collection value in a class constructor.
    Public Sub New()
        SetValue(s_aquariumContentsPropertyKey, New FreezableCollection(Of FrameworkElement)())
    End Sub

    ' Declare a public get accessor.
    Public ReadOnly Property AquariumContents As FreezableCollection(Of FrameworkElement)
        Get
            Return CType(GetValue(s_aquariumContentsPropertyKey.DependencyProperty), FreezableCollection(Of FrameworkElement))
        End Get
    End Property
End Class

Voir aussi