Abhängigkeitseigenschaften vom AuflistungstypCollection-Type Dependency Properties

Dieses Thema enthält einen Leitfaden und empfohlene Muster zur Implementierung einer Abhängigkeitseigenschaft für Eigenschaften vom Auflistungstyp.This topic provides guidance and suggested patterns for how to implement a dependency property where the type of the property is a collection type.

Implementieren einer Abhängigkeitseigenschaft vom AuflistungstypImplementing a Collection-Type Dependency Property

Für eine Abhängigkeitseigenschaft im Allgemeinen Implementierung, die Sie ausführen, wird Sie definieren eine CLRCLR Eigenschaftenwrapper, in dem diese Eigenschaft basiert auf einer DependencyProperty -Bezeichner statt ein Feld oder anderes Konstrukt.For a dependency property in general, the implementation pattern that you follow is that you define a CLRCLR property wrapper, where that property is backed by a DependencyProperty identifier rather than a field or other construct. Folgen Sie diesem Muster auch bei der Implementierung einer Eigenschaft vom Auflistungstyp.You follow this same pattern when you implement a collection-type property. Eine Eigenschaft vom Auflistungstyp führt jedoch komplexer, das Muster, wenn der Typ, der in der Auflistung enthalten ist, die selbst ist ein DependencyObject oder Freezable abgeleitete Klasse.However, a collection-type property introduces some complexity to the pattern whenever the type that is contained within the collection is itself a DependencyObject or Freezable derived class.

Initialisieren der Auflistung für einen anderen Wert als den StandardwertInitializing the Collection Beyond the Default Value

Beim Erstellen einer Abhängigkeitseigenschaft geben Sie nicht den Standardwert der Eigenschaft als Anfangsfeldwert an.When you create a dependency property, you do not specify the property default value as the initial field value. Geben Sie den Standardwert stattdessen über die Metadaten für Abhängigkeitseigenschaften an.Instead, you specify the default value through the dependency property metadata. Handelt es sich bei der Eigenschaft um einen Verweistyp, ist der in den Metadaten für Abhängigkeitseigenschaften angegebene Standardwert kein Standardwert pro Instanz. Er stellt vielmehr einen Standardwert dar, der für alle Instanzen des Typs gilt.If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type. Achten Sie also darauf, dass Sie nicht die durch die Metadaten für Auflistungseigenschaften definierte, einzelne statische Auflistung als funktionierenden Standardwert für neu erstellte Instanzen des Typs verwenden.Therefore you must be careful to not use the singular static collection defined by the collection property metadata as the working default value for newly created instances of your type. Vergewissern Sie sich stattdessen, dass Sie den Auflistungswert bewusst als eine eindeutige (Instanz-)Auflistung als Teil Ihrer Klassenkonstruktorlogik festlegen.Instead, you must make sure that you deliberately set the collection value to a unique (instance) collection as part of your class constructor logic. Andernfalls erstellen Sie unbeabsichtigterweise eine Singleton-Klasse.Otherwise you will have created an unintentional singleton class.

Betrachten Sie das folgende Beispiel.Consider the following example. Der nachfolgende Abschnitt des Beispiels veranschaulicht die Definition einer Aquarium-Klasse.The following section of the example shows the definition for a class Aquarium. Die Klasse definiert die Auflistung Typ Abhängigkeitseigenschaft AquariumObjects, verwendet die generische List<T> Typ mit einem FrameworkElement typeinschränkung.The class defines the collection type dependency property AquariumObjects, which uses the generic List<T> type with a FrameworkElement type constraint. In der Register(String, Type, Type, PropertyMetadata) -Aufruf für die Abhängigkeitseigenschaft, die Metadaten legt den Standardwert als neue generische List<T>.In the Register(String, Type, Type, PropertyMetadata) call for the dependency property, the metadata establishes the default value to be a new generic List<T>.

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); }
    }

    // ...

}
Public Class Fish
    Inherits FrameworkElement
End Class
Public Class Aquarium
    Inherits DependencyObject
    Private Shared ReadOnly AquariumContentsPropertyKey As DependencyPropertyKey = DependencyProperty.RegisterReadOnly("AquariumContents", GetType(List(Of FrameworkElement)), GetType(Aquarium), New FrameworkPropertyMetadata(New List(Of FrameworkElement)()))
    Public Shared ReadOnly AquariumContentsProperty As DependencyProperty = AquariumContentsPropertyKey.DependencyProperty

    Public ReadOnly Property AquariumContents() As List(Of FrameworkElement)
        Get
            Return CType(GetValue(AquariumContentsProperty), List(Of FrameworkElement))
        End Get
    End Property

    ' ...

End Class

Wenn Sie den Code unverändert lassen, wird dieser einzelne Listenstandardwert für alle Instanzen von Aquarium freigegeben.However, if you just left the code as shown, that single list default value is shared for all instances of Aquarium. Beim Ausführen des folgenden Testcodes, der zeigen soll, wie zwei getrennte Aquarium-Instanzen instanziiert und jeder ein einzelner unterschiedlicher Fish hinzugefügt würde, käme es zu einem überraschenden Ergebnis:If you ran the following test code, which is intended to show how you would instantiate two separate Aquarium instances and add a single different Fish to each of them, you would see a surprising result:

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");
Dim myAq1 As New Aquarium()
Dim myAq2 As New Aquarium()
Dim f1 As New Fish()
Dim f2 As 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")

Anstatt eines Eintrags besitzt jede Auflistung nun zwei!Instead of each collection having a count of one, each collection has a count of two! Das liegt daran, dass jedes Aquarium seinen Fish der Standardwertauflistung hinzugefügt hat, die das Ergebnis eines einzelnen Konstruktoraufrufs in den Metadaten ist und daher für alle Instanzen freigegeben ist.This is because each Aquarium added its Fish to the default value collection, which resulted from a single constructor call in the metadata and is therefore shared between all instances. Diese Situation ist so gut wie nie erwünscht.This situation is almost never what you want.

Zur Behebung des Problems müssen Sie den Abhängigkeitseigenschaftswert der Auflistung auf eine eindeutige Instanz als Teil des Klassenkonstruktoraufrufs zurücksetzen.To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call. Da die Eigenschaft eine schreibgeschützte Abhängigkeitseigenschaft ist, verwenden Sie die SetValue(DependencyPropertyKey, Object) Methode, mit der DependencyPropertyKey , die nur innerhalb der Klasse zugänglich ist.Because the property is a read-only dependency property, you use the SetValue(DependencyPropertyKey, Object) method to set it, using the DependencyPropertyKey that is only accessible within the class.

public Aquarium() : base()
{
    SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>()); 
}
Public Sub New()
    MyBase.New()
    SetValue(AquariumContentsPropertyKey, New List(Of FrameworkElement)())
End Sub

Bei einer erneuten Ausführung des Testcodes wären die Ergebnisse nun eher wie erwartet, da jedes Aquarium seine eigene eindeutige Auflistung unterstützen würde.Now, if you ran that same test code again, you could see more expected results, where each Aquarium supported its own unique collection.

Dieses Muster wäre geringfügig abgewandelt, wenn Sie die Auflistungseigenschaft mit Lese-/Schreibzugriff wählen würden.There would be a slight variation on this pattern if you chose to have your collection property be read-write. In diesem Fall könnten Sie den öffentlichen Set-Accessor aufrufen, aus dem Konstruktor, um die Initialisierung auszuführen, die weiterhin die nichtschlüsselsignatur von Aufrufen SetValue(DependencyProperty, Object) innerhalb Ihres Set-Wrappers mithilfe einer öffentlichen DependencyProperty Bezeichner.In that case, you could call the public set accessor from the constructor to do the initialization, which would still be calling the nonkey signature of SetValue(DependencyProperty, Object) within your set wrapper, using a public DependencyProperty identifier.

Melden von Bindungswertänderungen durch AuflistungseigenschaftenReporting Binding Value Changes from Collection Properties

Eine Auflistungseigenschaft, die selbst eine Abhängigkeitseigenschaft ist, meldet nicht automatisch Änderungen an die ihr untergeordneten Eigenschaften.A collection property that is itself a dependency property does not automatically report changes to its subproperties. Wenn Sie in einer Auflistung Bindungen erstellen, kann möglicherweise das Melden von Änderungen durch die Bindung verhindert werden, wodurch einige Datenbindungsszenarios ungültig gemacht werden können.If you are creating bindings into a collection, this can prevent the binding from reporting changes, thus invalidating some data binding scenarios. Allerdings bei der Verwendung des Auflistungstyps FreezableCollection<T> als Ihren Auflistungstyp dann Untereigenschaften-Änderungen an den darin enthaltenen Elemente in der Auflistung ordnungsgemäß gemeldet, und Bindung funktioniert wie erwartet.However, if you use the collection type FreezableCollection<T> as your collection type, then subproperty changes to contained elements in the collection are properly reported, and binding works as expected.

Um die Bindung von untergeordneten Eigenschaften in einer Auflistung zu ermöglichen, erstellen Sie die Auflistungseigenschaft als Typ FreezableCollection<T>, mit einer typeinschränkung für diese Sammlung in einen DependencyObject abgeleitete Klasse.To enable subproperty binding in a dependency object collection, create the collection property as type FreezableCollection<T>, with a type constraint for that collection to any DependencyObject derived class.

Siehe auchSee also