Freigeben über


Abhängigkeitseigenschaften vom Auflistungstyp

In diesem Thema finden Sie Unterstützung und Mustervorschläge für das Implementieren einer Abhängigkeitseigenschaft, wenn die Eigenschaft zum Auflistungstyp gehört.

Dieses Thema enthält folgende Abschnitte.

  • Implementieren einer Abhängigkeitseigenschaft vom Auflistungstyp
  • Initialisieren der Auflistung mit einem anderem Wert als dem Standardwert
  • Melden von Bindungswertänderungen durch Auflistungseigenschaften
  • Verwandte Abschnitte

Implementieren einer Abhängigkeitseigenschaft vom Auflistungstyp

Für Abhängigkeitseigenschaften wird im Allgemeinen folgendes Implementierungsmuster verwendet: Sie definieren einen CLR-Eigenschaftenwrapper, in dem diese Eigenschaft durch einen DependencyProperty-Bezeichner statt durch ein Feld oder ein anderes Konstrukt unterstützt wird. Dieses Muster gilt auch, wenn Sie eine Eigenschaft vom Auflistungstyp implementieren. Bei einer Eigenschaft vom Auflistungstyp wird dieses Muster jedoch komplexer, wenn der in der Auflistung enthaltene Typ selbst ein DependencyObject oder eine von Freezable abgeleitete Klasse ist.

Initialisieren der Auflistung mit einem anderem Wert als dem Standardwert

Wenn Sie eine Abhängigkeitseigenschaft erstellen, geben Sie den Standardwert der Eigenschaft nicht als anfänglichen Feldwert an. Stattdessen geben Sie den Standardwert über die Metadaten für die Abhängigkeitseigenschaft an. Wenn die Eigenschaft ein Verweistyp ist, so ist der in den Metadaten für die Abhängigkeitseigenschaft angegebene Standardwert kein Standardwert pro Instanz, sondern ein Standardwert, der für alle Instanzen dieses Typs gilt. Daher müssen Sie sorgfältig darauf achten, dass Sie nicht die durch die Metadaten für die Auflistungseigenschaft definierte einzelne statische Auflistung als funktionierenden Standardwert für neu erstellte Instanzen Ihres Typs verwenden. Stattdessen müssen Sie sicherstellen, dass Sie den Auflistungswert bewusst auf eine eindeutige (Instanz-) Auflistung im Rahmen der Klassenkonstruktorlogik festlegen. Andernfalls haben Sie eine unbeabsichtigte Singletonklasse erstellt.

Betrachten Sie das folgende Beispiel. Im nachstehenden Abschnitt des Beispiels wird die Definition für eine Aquarium-Klasse veranschaulicht. Die Klasse definiert die AquariumObjects-Abhängigkeitseigenschaft vom Auflistungstyp, die den generischen List<T>-Typ mit einer FrameworkElement-Typeinschränkung verwendet. Im Register(String, Type, Type, PropertyMetadata)-Aufruf für die Abhängigkeitseigenschaft legen die Metadaten den Standardwert als neue generische List<T> fest.

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


...


}

Wenn Sie jedoch den Code einfach so wie dargestellt lassen, wird der einzelne Listenstandardwert für alle Instanzen von Aquarium freigegeben. Beim Ausführen des folgenden Testcodes, der zeigen soll, wie Sie zwei separate Aquarium-Instanzen instanziieren und jeder einen einzelnen unterschiedlichen Fish hinzufügen würden, wäre das Ergebnis überraschend:

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

Statt über einen Eintrag in jeder Auflistung verfügt jede Auflistung über zwei Einträge! Dies liegt daran, dass jedes Aquarium seinen Fish der Standardwertauflistung hinzugefügt hat, die aus einem einzigen Konstruktoraufruf in den Metadaten resultiert und daher für alle Instanzen freigegeben ist. Diese Situation ist in nahezu keinem Fall erwünscht.

Um dieses Problem zu beheben, müssen Sie für den Abhängigkeitseigenschaftswert der Auflistung eine eindeutige Instanz im Rahmen der Klassenkonstruktorlogik festlegen. Da diese Eigenschaft eine schreibgeschützte Abhängigkeitseigenschaft ist, verwenden Sie zur Festlegung die SetValue(DependencyPropertyKey, Object)-Methode, mit dem DependencyPropertyKey, der nur innerhalb der Klasse verfügbar ist.

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

Wenn Sie nun denselben Test erneut ausführen würden, entsprächen die Ergebnisse eher Ihren Erwartungen, da jedes Aquarium jetzt seine eigene eindeutige Auflistung unterstützt.

Dieses Muster sieht geringfügig anders aus, wenn Sie sich für eine nicht schreibgeschützte Auflistungseigenschaft entscheiden. In diesem Fall rufen Sie den öffentlichen set-Accessor vom Konstruktor aus auf, um die Initialisierung durchzuführen. Dadurch würde weiterhin die Nicht-Schlüssel-Signatur von SetValue(DependencyProperty, Object) innerhalb Ihres set-Wrappers aufgerufen, mit einem öffentlichen DependencyProperty-Bezeichner.

Melden von Bindungswertänderungen durch Auflistungseigenschaften

Eine Auflistungseigenschaft, die selbst eine Abhängigkeitseigenschaft ist, meldet nicht automatisch Änderungen an ihren untergeordneten Eigenschaften. Wenn Sie Bindungen in einer Auflistung erstellen, kann dies die Bindung daran hindern, Änderungen zu melden, sodass einige Datenbindungsszenarien ungültig gemacht werden. Wenn Sie jedoch den FreezableCollection<T>-Auflistungstyp als Ihren Auflistungstyp verwenden, werden Änderungen an untergeordneten Elementen in der Auflistung ordnungsgemäß gemeldet, und die Bindung funktioniert wie erwartet.

Um die Bindung von untergeordneten Eigenschaften in einem Abhängigkeitsobjekt zu aktivieren, erstellen Sie die Auflistungseigenschaft als FreezableCollection<T>-Typ mit einer Typeinschränkung für diese Auflistung bezüglich jeder vom DependencyObject abgeleiteten Klasse.

Siehe auch

Referenz

FreezableCollection<T>

Konzepte

XAML- und benutzerdefinierte Klassen für WPF

Übersicht über Datenbindung

Übersicht über Abhängigkeitseigenschaften

Benutzerdefinierte Abhängigkeitseigenschaften

Metadaten für Abhängigkeitseigenschaften