Compartilhar via


Propriedades de dependência de tipo de coleção (WPF .NET)

Este artigo fornece diretrizes e padrões sugeridos para implementar uma propriedade de dependência que é um tipo de coleção.

Pré-requisitos

O artigo pressupõe um conhecimento básico das propriedades de dependência e que você leu Visão geral das propriedades de dependência. Para seguir os exemplos neste artigo, é útil se você estiver familiarizado com XAML (Extensible Application Markup Language) e souber como escrever aplicativos WPF.

Implementar uma propriedade de dependência de tipo de coleção

Em geral, o padrão de implementação de uma propriedade de dependência é um wrapper de propriedade CLR apoiado por um DependencyProperty identificador em vez de um campo ou outro constructo. Você pode seguir o mesmo padrão ao implementar uma propriedade de dependência do tipo coleção. O padrão será mais complexo se o tipo de elemento de coleção for uma DependencyObject classe ou uma Freezable classe derivada.

Inicializar a coleção

Ao criar uma propriedade de dependência, você normalmente especifica o valor padrão por meio de metadados de propriedade de dependência em vez de especificar um valor de propriedade inicial. No entanto, se o valor da propriedade for um tipo de referência, o valor padrão deverá ser definido no construtor da classe que registra a propriedade de dependência. Os metadados da propriedade de dependência não devem incluir um valor de tipo de referência padrão porque esse valor será atribuído a todas as instâncias da classe, criando uma classe singleton.

O exemplo a seguir declara uma Aquarium classe que contém uma coleção de FrameworkElement elementos em um arquivo .List<T> Um valor de coleção padrão não é incluído no PropertyMetadata passado para o RegisterReadOnly(String, Type, Type, PropertyMetadata) método e, em vez disso, o construtor de classe é usado para definir o valor de coleção padrão como um novo .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

O código de teste a seguir instancia duas instâncias separadas Aquarium e adiciona um item diferente Fish a cada coleção. Se você executar o código, verá que cada Aquarium instância tem um único item de coleção, conforme esperado.

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

Mas, se você comentar o construtor da classe e passar o valor de coleção padrão quanto PropertyMetadata ao RegisterReadOnly(String, Type, Type, PropertyMetadata) método, verá que cada Aquarium instância recebe dois itens de coleção! Isso ocorre porque ambas as Fish instâncias são adicionadas à mesma lista, que é compartilhada por todas as instâncias da classe Aquarium. Portanto, quando a intenção é que cada instância de objeto tenha sua própria lista, o valor padrão deve ser definido no construtor da classe.

Inicializar uma coleção de leitura/gravação

O exemplo a seguir declara uma propriedade de dependência do tipo coleção de leitura/gravação na Aquarium classe, usando os métodos Register(String, Type, Type) de assinatura não chave 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

Propriedades de dependência FreezableCollection

Uma propriedade de dependência de tipo de coleção não relata automaticamente alterações em suas subpropriedades. Como resultado, se você estiver associando a uma coleção, a associação poderá não relatar alterações, invalidando alguns cenários de associação de dados. Mas, se você usar FreezableCollection<T> para o tipo de propriedade de dependência, as alterações nas propriedades dos elementos de coleção serão relatadas corretamente e a associação funcionará conforme o esperado.

Para habilitar a associação de subpropriedade em uma coleção de objetos de dependência, use o tipo FreezableCollectionde coleção , com uma restrição de tipo de qualquer DependencyObject classe derivada.

O exemplo a seguir declara uma Aquarium classe que contém uma FreezableCollection com uma restrição de tipo de FrameworkElement. Um valor de coleção padrão não é incluído no PropertyMetadata passado para o RegisterReadOnly(String, Type, Type, PropertyMetadata) método e, em vez disso, o construtor de classe é usado para definir o valor de coleção padrão como um novo 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

Confira também