Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo fornisce indicazioni e modelli suggeriti per l'implementazione di una proprietà di dipendenza che è un tipo di raccolta.
Prerequisiti
L'articolo presuppone una conoscenza di base delle proprietà di dipendenza e che tu abbia letto "Panoramica delle proprietà di dipendenza". Per seguire gli esempi in questo articolo, è utile se si ha familiarità con Extensible Application Markup Language (XAML) e si sa come scrivere applicazioni WPF.
Implementare una proprietà di dipendenza di tipo raccolta
In generale, il modello di implementazione per una proprietà di dipendenza è un wrapper di proprietà CLR supportato da un identificatore DependencyProperty anziché da un campo o da un altro costrutto. È possibile seguire lo stesso modello quando si implementa una proprietà di dipendenza di tipo raccolta. Il modello è più complesso se il tipo di elemento della raccolta è un DependencyObject o una classe derivata Freezable.
Inizializzare la raccolta
Quando si crea una proprietà di dipendenza, in genere si specifica il valore predefinito tramite i metadati della proprietà di dipendenza anziché specificare un valore iniziale della proprietà. Tuttavia, se il valore della proprietà è un tipo riferimento, il valore predefinito deve essere impostato nel costruttore della classe che registra la proprietà di dipendenza. I metadati della proprietà di dipendenza non devono includere un valore di tipo riferimento predefinito perché tale valore verrà assegnato a tutte le istanze della classe, creando una classe singleton.
Nell'esempio seguente viene dichiarata una classe Aquarium
che contiene una raccolta di elementi FrameworkElement in un List<T>generico . Un valore di raccolta predefinito non è incluso nel PropertyMetadata passato al metodo RegisterReadOnly(String, Type, Type, PropertyMetadata); invece, si utilizza il costruttore della classe per impostare il valore di raccolta predefinito su un nuovo List
generico.
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
Il codice di test seguente crea due istanze separate di Aquarium
e aggiunge un elemento Fish
diverso a ogni raccolta. Se esegui il codice, vedrai che ogni istanza di Aquarium
ha un singolo elemento della raccolta, come previsto.
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
Tuttavia, se si commenta il costruttore della classe e si passa il valore di raccolta predefinito come PropertyMetadata al metodo RegisterReadOnly(String, Type, Type, PropertyMetadata), si noterà che ogni istanza di Aquarium
ottiene due elementi della raccolta. Questo perché entrambe le istanze di Fish
vengono aggiunte allo stesso elenco, che è condiviso da tutte le istanze della classe Aquarium. Pertanto, quando l'intento è che ciascuna istanza dell'oggetto abbia la propria lista, il valore predefinito deve essere impostato nel costruttore della classe.
Inizializzare una raccolta di lettura/scrittura
Nell'esempio seguente viene dichiarata una proprietà di dipendenza di tipo lettura/scrittura della raccolta nella classe Aquarium
utilizzando i metodi di firma non chiave Register(String, Type, Type) e 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
Proprietà di dipendenza della FreezableCollection
Una proprietà di dipendenza di tipo raccolta non segnala automaticamente le modifiche nelle relative proprietà secondarie. Di conseguenza, se si esegue il binding a una raccolta, l'associazione potrebbe non registrare cambiamenti, invalidando alcuni scenari di data binding. Tuttavia, se si usa FreezableCollection<T> per il tipo di proprietà di dipendenza, le modifiche apportate alle proprietà degli elementi della raccolta vengono segnalate correttamente e il binding funziona come previsto.
Per abilitare l'associazione di sotto-proprietà in una raccolta di oggetti di dipendenza, utilizzare il tipo di raccolta FreezableCollection
, con un vincolo di tipo su qualsiasi classe derivata da DependencyObject.
Nell'esempio seguente viene dichiarata una classe Aquarium
che contiene un FreezableCollection
con un vincolo di tipo FrameworkElement. Un valore di raccolta predefinito non è incluso nel PropertyMetadata passato al metodo RegisterReadOnly(String, Type, Type, PropertyMetadata); al contrario, viene utilizzato il costruttore della classe per assegnare al valore di raccolta predefinito un nuovo 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
Vedere anche
.NET Desktop feedback