Share via


Propiedades de dependencia de tipo de colección

En este se proporciona orientación y sugerencias de modelos de implementación de una propiedad de dependencia cuyo tipo sea de colección.

Este tema contiene las secciones siguientes.

  • Implementar una propiedad de dependencia de tipo de colección
  • Inicializar la colección más allá del valor predeterminado
  • Comunicar los cambios de valores de enlace de propiedades de colección
  • Temas relacionados

Implementar una propiedad de dependencia de tipo de colección

Para las propiedades de dependencia en general, el modelo de implementación que se sigue consiste en definir un contenedor de propiedad CLR, donde esa propiedad está respaldada por un identificador de DependencyProperty, en lugar de por un campo u otra construcción. Este mismo modelo se sigue al implementar una propiedad de tipo de colección. Sin embargo, una propiedad de tipo de colección presenta cierta complejidad en el modelo cada vez el tipo contenido en la colección es, a su vez, una clase derivada de DependencyObject o Freezable.

Inicializar la colección más allá del valor predeterminado

Al crear una propiedad de dependencia, no se especifica el valor predeterminado de la misma como el valor inicial del campo. En su lugar, se especifica el valor predeterminado a través de los metadatos de la propiedad de dependencia. Si la propiedad es un tipo de referencia, el valor predeterminado especificado en sus metadatos no es un valor predeterminado de una instancia individual, sino un valor predeterminado que se aplica a todas las instancias del tipo. Por consiguiente, debe extremar las precauciones para no utilizar la colección estática singular definida por los metadatos de la propiedad de colección como el valor predeterminado activo de las instancias recién creadas de su tipo. En vez de eso, debe asegurarse de establecer de manera deliberada el valor de colección en una colección única (instancia) como parte de la lógica del constructor de clase. De lo contrario, habrá creado involuntariamente una clase singleton.

Considere el ejemplo siguiente. En la sección siguiente del ejemplo se muestra la definición de una clase Aquarium. La clase define la propiedad de dependencia de tipo de colección AquariumObjects, que utiliza el tipo List<T> genérico con una restricción de tipo FrameworkElement. En la llamada a Register(String, Type, Type, PropertyMetadata) para la propiedad de dependencia, los metadatos establecen el valor predeterminado en un nuevo objeto List<T> genérico.

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


...


}

Sin embargo, si se limita a dejar el código tal y como se muestra, ese valor predeterminado único de la lista se compartirá para todas las instancias de Aquarium. Si ejecutara el código de pruebas siguiente, diseñado para mostrar cómo crear dos instancias independientes de Aquarium y agregar un tipo Fish único diferente a cada una de ellas, vería un resultado sorprendente:

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

¡En lugar de que el recuento de cada colección sea de uno, el recuento de cada colección es de dos! Esto se debe a que cada Aquarium agregó Fish a la colección de valores predeterminados, resultante de una llamada de constructor única en los metadatos, con lo que se comparte entre todas las instancias. Esta situación casi nunca es deseable.

Para corregir este problema, debe restablecer el valor de la propiedad de dependencia de colección en una instancia única, como parte de la llamada al constructor de clase. Dado que la propiedad es de dependencia y de sólo lectura, se utiliza el método SetValue(DependencyPropertyKey, Object) para establecerla, utilizando una clave DependencyPropertyKey que sólo está accesible dentro de la clase.

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

Ahora, si ejecuta de nuevo el mismo código de pruebas de antes, los resultados serían más previsibles, ya que Aquarium tiene su propia colección única.

Habría una ligera variación en el modelo si optase por hacer la propiedad de colección de lectura y escritura. En ese caso, podría llamar al descriptor de acceso set público desde el constructor para llevar a cabo la inicialización, en la que igualmente se llamaría a la firma sin clave de SetValue(DependencyProperty, Object) dentro del contenedor set, utilizando un identificador de DependencyProperty público.

Comunicar los cambios de valores de enlace de propiedades de colección

Una propiedad de colección que también es de dependencia no comunica automáticamente los cambios a sus subpropiedades. Si crea enlaces en una colección, esto puede evitar que el enlace comunique los cambios, lo que invalida algunos escenarios de enlace de datos. Sin embargo, si utiliza el tipo de colección FreezableCollection<T>, entonces sí se comunican correctamente los cambios de las subpropiedades a los elementos contenidos en la colección y el enlace funciona como está previsto.

Para habilitar el enlace de subpropiedades en una colección de objetos de dependencia, cree la propiedad de colección como el tipo FreezableCollection<T>, con una restricción de tipo para esa colección a cualquier clase derivada de DependencyObject.

Vea también

Referencia

FreezableCollection<T>

Conceptos

Clases XAML y personalizadas para WPF

Información general sobre el enlace de datos

Información general sobre las propiedades de dependencia

Propiedades de dependencia personalizadas

Metadatos de las propiedades de dependencia