Freigeben über


Abhängigkeitseigenschaften vom Sammlungstyp

Dieser Artikel enthält Anleitungen und vorgeschlagene Muster für die Implementierung einer Abhängigkeitseigenschaft, die ein Sammlungstyp ist.

Voraussetzungen

In diesem Artikel wird davon ausgegangen, dass Sie über grundlegende Kenntnisse von Abhängigkeitseigenschaften verfügen und eine Übersicht über Abhängigkeitseigenschaften gelesen haben. Um den Beispielen in diesem Artikel zu folgen, hilft es Ihnen, wenn Sie mit Extensible Application Markup Language (XAML) vertraut sind und wissen, wie WPF-Anwendungen geschrieben werden.

Implementieren einer Abhängigkeitseigenschaft vom Typ "Sammlung"

Im Allgemeinen ist das Implementierungsmuster für eine Abhängigkeitseigenschaft ein CLR-Eigenschaftswrapper, der von einem DependencyProperty Bezeichner anstelle eines Felds oder eines anderen Konstrukts unterstützt wird. Sie können demselben Muster folgen, wenn Sie eine Abhängigkeitseigenschaft vom Typ "Sammlung" implementieren. Das Muster ist komplexer, wenn der Sammlungselementtyp eine DependencyObject oder eine Freezable abgeleitete Klasse ist.

Die Auflistung initialisieren

Wenn Sie eine Abhängigkeitseigenschaft erstellen, geben Sie in der Regel den Standardwert über Abhängigkeitseigenschaftsmetadaten an, anstatt einen anfänglichen Eigenschaftswert anzugeben. Wenn der Eigenschaftswert jedoch ein Verweistyp ist, sollte der Standardwert im Konstruktor der Klasse festgelegt werden, die die Abhängigkeitseigenschaft registriert. Die Metadaten der Abhängigkeitseigenschaft sollten keinen Standardverweistypwert enthalten, da dieser Wert allen Instanzen der Klasse zugewiesen wird und eine Singleton-Klasse erstellt wird.

Im folgenden Beispiel wird eine Aquarium Klasse deklariert, die eine Auflistung von FrameworkElement Elementen in einer generischen List<T>Klasse enthält. Ein Standardwert der Sammlung ist nicht in der an die PropertyMetadata-Methode übergebenen RegisterReadOnly(String, Type, Type, PropertyMetadata) enthalten, und stattdessen wird der Klassenkonstruktor verwendet, um den Standardwert der Sammlung auf einen neuen generischen List Wert festzulegen.

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

Der folgende Testcode instanziiert zwei separate Aquarium Instanzen und fügt jeder Auflistung ein anderes Fish Element hinzu. Wenn Sie den Code ausführen, sehen Sie, dass jede Aquarium Instanz wie erwartet über ein einzelnes Sammlungselement verfügt.

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

Wenn Sie den Klassenkonstruktor auskommentieren und den Standardwert der Sammlung als PropertyMetadata in die Methode RegisterReadOnly(String, Type, Type, PropertyMetadata) übergeben, sehen Sie, dass jede Aquarium Instanz zwei Sammlungselemente erhält! Dies liegt daran, dass beide Fish Instanzen derselben Liste hinzugefügt werden, die von allen Instanzen der Aquarium-Klasse gemeinsam verwendet wird. Wenn also die Absicht besteht, dass jede Objektinstanz ihre eigene Liste hat, sollte der Standardwert im Klassenkonstruktor festgelegt werden.

Initialisieren einer Sammlung mit Lese- und Schreibzugriff

Das folgende Beispiel deklariert eine schreibbare Abhängigkeitseigenschaft vom Sammlungstyp in der Aquarium Klasse, unter Verwendung der non-key Unterschriftenmethoden Register(String, Type, Type) und 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

FreezableCollection-Abhängigkeitseigenschaften

Eine Abhängigkeitseigenschaft vom Sammlungstyp meldet keine Änderungen an ihren Untereigenschaften automatisch. Wenn Sie eine Bindung an eine Sammlung vornehmen, meldet die Bindung möglicherweise keine Änderungen, wodurch einige Datenbindungsszenarien ungültig werden. Wenn Sie jedoch für den Abhängigkeitseigenschaftstyp verwenden FreezableCollection<T> , werden Änderungen an den Eigenschaften von Sammlungselementen ordnungsgemäß gemeldet und die Bindung funktioniert wie erwartet.

Um die Untereigenschaftsbindung in einer Auflistung von Abhängigkeitsobjekten zu aktivieren, verwenden Sie den Sammlungstyp FreezableCollectionmit einer Typeinschränkung jeder DependencyObject abgeleiteten Klasse.

Im folgenden Beispiel wird eine Aquarium-Klasse deklariert, die ein FreezableCollection mit einer Typeneinschränkung von FrameworkElement enthält. Ein Standardauflistungswert ist nicht in der PropertyMetadata an die RegisterReadOnly(String, Type, Type, PropertyMetadata) Methode übergebenen Auflistung enthalten, und stattdessen wird der Klassenkonstruktor verwendet, um den Standardauflistungswert auf einen neuen FreezableCollectionfestzulegen.

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

Siehe auch