Proprietà di dipendenza di tipo raccoltaCollection-Type Dependency Properties

Questo argomento include linee guida e modelli consigliati per l'implementazione di una proprietà di dipendenza in cui la proprietà è di tipo raccolta.This topic provides guidance and suggested patterns for how to implement a dependency property where the type of the property is a collection type.

Implementazione di una proprietà di dipendenza di tipo raccoltaImplementing a Collection-Type Dependency Property

Per una proprietà di dipendenza in generale, il modello di implementazione che seguono è definito un CLRCLR wrapper della proprietà, in cui tale proprietà è supportata da un DependencyProperty identificatore anziché un campo o altro costrutto.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. Questo stesso modello viene seguito quando si implementa una proprietà di tipo raccolta.You follow this same pattern when you implement a collection-type property. Tuttavia, una proprietà di tipo di raccolta introduce un livello di complessità al modello ogni volta che il tipo di contenuto all'interno della raccolta è a sua volta un DependencyObject o Freezable classe derivata.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.

Inizializzazione della raccolta oltre il valore predefinitoInitializing the Collection Beyond the Default Value

Quando si crea una proprietà di dipendenza, non si specifica il valore predefinito della proprietà come valore iniziale del campo.When you create a dependency property, you do not specify the property default value as the initial field value. Al contrario, viene specificato il valore predefinito tramite i metadati della proprietà di dipendenza.Instead, you specify the default value through the dependency property metadata. Se la proprietà è un tipo di riferimento, il valore predefinito specificato nei metadati della proprietà di dipendenza non è un valore predefinito per istanza, ma un valore predefinito che viene applicato a tutte le istanze del tipo.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. Pertanto, è necessario prestare attenzione a non usare la singola raccolta statica definita dai metadati della proprietà della raccolta come valore di lavoro predefinito per le istanze del tipo appena create.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. È invece necessario assicurarsi di impostare deliberatamente il valore della raccolta su un'unica raccolta (istanza), come parte della logica del costruttore di classi.Instead, you must make sure that you deliberately set the collection value to a unique (instance) collection as part of your class constructor logic. In caso contrario, verrà creata una classe Singleton non intenzionale.Otherwise you will have created an unintentional singleton class.

Si osservi l'esempio riportato di seguito.Consider the following example. La sezione seguente dell'esempio descrive la definizione di una classe Aquarium.The following section of the example shows the definition for a class Aquarium. La classe definisce le proprietà di dipendenza di tipo raccolta AquariumObjects, che utilizza il tipo generico List<T> tipo con un FrameworkElement vincolo di tipo.The class defines the collection type dependency property AquariumObjects, which uses the generic List<T> type with a FrameworkElement type constraint. Nel Register(String, Type, Type, PropertyMetadata) chiamata per la proprietà di dipendenza, i metadati stabilisce il valore predefinito per un nuovo oggetto generico 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

Tuttavia, se si lascia inalterato il codice, quel singolo valore predefinito dell'elenco viene condiviso per tutte le istanze di Aquarium.However, if you just left the code as shown, that single list default value is shared for all instances of Aquarium. Se si esegue il codice di test riportato di seguito, destinato a mostrare la modalità di creazione di due istanze di Aquarium separate e si aggiunge un singolo oggetto Fish diverso a ciascuna di esse, il risultato è sorprendente: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")

Invece di contenere un solo elemento, ogni raccolta ne contiene due.Instead of each collection having a count of one, each collection has a count of two! Questa situazione si verifica in quanto ciascun oggetto Aquarium ha aggiunto il proprio oggetto Fish alla raccolta dei valori predefiniti, generata da una singola chiamata al costruttore nei metadati e pertanto condivisa tra tutte le istanze.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. Si tratta di una situazione quasi mai auspicabile.This situation is almost never what you want.

Per risolvere questo problema, è necessario reimpostare il valore della proprietà di dipendenza della raccolta su un'unica istanza, come parte della chiamata al costruttore di classe.To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call. Poiché la proprietà è una proprietà di dipendenza di sola lettura, usare il SetValue(DependencyPropertyKey, Object) metodo per impostare, usando il DependencyPropertyKey che è accessibile solo all'interno della classe.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

A questo punto, se si esegue nuovamente questo stesso codice di test, è possibile ottenere i risultati previsti, in cui ciascun oggetto Aquarium supporta la propria raccolta univoca.Now, if you ran that same test code again, you could see more expected results, where each Aquarium supported its own unique collection.

Se si sceglie una proprietà della raccolta con possibilità di accesso in lettura/scrittura, questo modello presenta una piccola variazione.There would be a slight variation on this pattern if you chose to have your collection property be read-write. In tal caso, è possibile chiamare la funzione di accesso set pubblico del costruttore per eseguire l'inizializzazione, che comunque chiama la firma non chiave di SetValue(DependencyProperty, Object) all'interno del wrapper set, utilizzando un pubblico DependencyProperty identificatore.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.

Segnalazione di modifiche dei valori di binding nelle proprietà della raccoltaReporting Binding Value Changes from Collection Properties

Una proprietà della raccolta che costituisce essa stessa una proprietà di dipendenza non segnala automaticamente le modifiche per le relative sottoproprietà.A collection property that is itself a dependency property does not automatically report changes to its subproperties. La creazione di binding all'interno di una raccolta può impedire al binding di segnalare le modifiche, invalidando in tal modo alcuni scenari di data binding.If you are creating bindings into a collection, this can prevent the binding from reporting changes, thus invalidating some data binding scenarios. Tuttavia, se si utilizza il tipo di raccolta FreezableCollection<T> come tipo di raccolta, quindi le modifiche di sottoproprietà di elementi contenuti nella raccolta correttamente segnalate e associazione funziona come previsto.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.

Per abilitare l'associazione di sottoproprietà in una raccolta di oggetti dipendenza, creare la proprietà della raccolta come tipo FreezableCollection<T>, con un vincolo di tipo per la raccolta a qualsiasi DependencyObject classe derivata.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.

Vedere ancheSee Also

FreezableCollection<T>
Classi XAML e personalizzate per WPFXAML and Custom Classes for WPF
Panoramica sul data bindingData Binding Overview
Panoramica sulle proprietà di dipendenzaDependency Properties Overview
Proprietà di dipendenza personalizzateCustom Dependency Properties
Metadati delle proprietà di dipendenzaDependency Property Metadata