Propiedades de dependencia de tipo colección (WPF .NET)
En este artículo se proporcionan instrucciones y patrones sugeridos para implementar una propiedad de dependencia que sea un tipo colección.
Importante
La documentación de la guía de escritorio para .NET 7 y .NET 6 está en proceso de elaboración.
Requisitos previos
En el artículo se da por supuesto un conocimiento básico de las propiedades de dependencia y que ha leído Información general sobre las propiedades de dependencia. Para seguir los ejemplos de este artículo, resultará útil que esté familiarizado con lenguaje XAML y sepa cómo escribir aplicaciones WPF.
Implementación de una propiedad de dependencia de tipo colección
En general el patrón de implementación de una propiedad de dependencia es un contenedor de propiedades CLR con el respaldo de un identificador DependencyProperty en lugar de un campo u otra construcción. Puede seguir el mismo patrón al implementar una propiedad de dependencia de tipo colección. El patrón es más complejo si el tipo de elemento de colección es un DependencyObject o un Freezable una clase derivada.
Inicialización de la colección
Cuando se crea una propiedad de dependencia, normalmente se especifica el valor predeterminado mediante metadatos de propiedad de dependencia, en lugar de especificar un valor de propiedad inicial. Sin embargo, si el valor de propiedad es un tipo de referencia, el valor predeterminado debe establecerse en el constructor de la clase que registra la propiedad de dependencia. Los metadatos de la propiedad de dependencia no deben incluir un valor de tipo de referencia predeterminado, porque ese valor se asignará a todas las instancias de la clase, creando una clase singleton.
En el ejemplo siguiente se declara una clase Aquarium
que contiene una colección de elementos FrameworkElement en un genérico List<T>. Un valor de colección predeterminado no se incluye en el PropertyMetadata que se pasa al método RegisterReadOnly(String, Type, Type, PropertyMetadata) y, en su lugar, el constructor de clase se usa para establecer el valor de colección predeterminado en un nuevo genérico List
.
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
El código de prueba siguiente crea dos instancias independientes de Aquarium
y agrega un elemento diferente Fish
a cada colección. Si ejecuta el código, verá que cada instancia de Aquarium
tiene un único elemento de colección, como cabe esperar.
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
Pero si convierte en comentario el constructor de clase y pasa el valor de colección predeterminado PropertyMetadata al método RegisterReadOnly(String, Type, Type, PropertyMetadata), verá que cada instancia de Aquarium
obtiene dos elementos de colección. Esto se debe a que ambas instancias de Fish
se agregan a la misma lista, que comparten todas las instancias de la clase Aquarium. Por lo tanto, cuando la intención es que cada instancia de objeto tenga su propia lista, el valor predeterminado debe establecerse en el constructor de clase.
Inicialización de una colección de lectura y escritura
En el ejemplo siguiente se declara una propiedad de dependencia de tipo colección de lectura y escritura en la clase Aquarium
mediante los métodos de firma sin clave Register(String, Type, Type) y 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
Propiedades de dependencia FreezableCollection
Una propiedad de dependencia de tipo colección no informa automáticamente de los cambios en sus subpropiedades. Como resultado, si va a enlazar a una colección, es posible que el enlace no informe de los cambios, lo que invalida algunos escenarios de enlace de datos. Sin embargo, si usa FreezableCollection<T> para el tipo de propiedad de dependencia, los cambios en las propiedades de los elementos de colección se notificarán correctamente y el enlace funcionará según lo previsto.
Para habilitar el enlace de subpropiedad en una colección de objetos de dependencia, use el tipo colección FreezableCollection
con una restricción de tipo de cualquier clase derivada DependencyObject.
En el ejemplo siguiente se declara una clase Aquarium
que contiene un FreezableCollection
con una restricción de tipo FrameworkElement. Un valor de colección predeterminado no se incluye en PropertyMetadata que se pasa al método RegisterReadOnly(String, Type, Type, PropertyMetadata) y, en su lugar, el constructor de clase se usa para establecer el valor de colección predeterminado en un nuevo 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
Vea también
.NET Desktop feedback
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de