Udostępnij za pośrednictwem


Właściwości zależności typu kolekcji (WPF .NET)

Ten artykuł zawiera wskazówki i sugerowane wzorce implementowania właściwości zależności, która jest typem kolekcji.

Ważne

Dokumentacja przewodnika dla komputerów dla platform .NET 7 i .NET 6 jest w budowie.

Wymagania wstępne

W tym artykule przyjęto założenie, że masz podstawową wiedzę na temat właściwości zależności i zapoznasz się z omówieniem właściwości zależności. Aby postępować zgodnie z przykładami w tym artykule, warto zapoznać się z językiem Extensible Application Markup Language (XAML) i wiedzieć, jak pisać aplikacje WPF.

Implementowanie właściwości zależności typu kolekcji

Ogólnie rzecz biorąc, wzorzec implementacji dla właściwości zależności to otoka właściwości CLR wspierana przez DependencyProperty identyfikator zamiast pola lub innej konstrukcji. Możesz postępować zgodnie z tym samym wzorcem podczas implementowania właściwości zależności typu kolekcji. Wzorzec jest bardziej złożony, jeśli typ elementu kolekcji to DependencyObject lub klasa pochodna Freezable .

Inicjowanie kolekcji

Podczas tworzenia właściwości zależności zazwyczaj określasz wartość domyślną za pomocą metadanych właściwości zależności zamiast określania początkowej wartości właściwości. Jeśli jednak wartość właściwości jest typem odwołania, wartość domyślna powinna zostać ustawiona w konstruktorze klasy, która rejestruje właściwość zależności. Metadane właściwości zależności nie powinny zawierać domyślnej wartości typu odwołania, ponieważ ta wartość zostanie przypisana do wszystkich wystąpień klasy, tworząc pojedynczą klasę.

Poniższy przykład deklaruje klasę Aquarium zawierającą kolekcję FrameworkElement elementów w klasie ogólnej List<T>. Domyślna wartość kolekcji nie jest uwzględniona w przekazanej PropertyMetadataRegisterReadOnly(String, Type, Type, PropertyMetadata) metodzie, a zamiast tego konstruktor klasy służy do ustawiania domyślnej wartości kolekcji na nową ogólną Listwartość .

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

Poniższy kod testowy tworzy wystąpienie dwóch oddzielnych Aquarium wystąpień i dodaje inny Fish element do każdej kolekcji. Jeśli uruchomisz kod, zobaczysz, że każde Aquarium wystąpienie ma jeden element kolekcji zgodnie z oczekiwaniami.

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

Jeśli jednak oznaczysz jako komentarz konstruktor klasy i przekażesz domyślną wartość kolekcji jako PropertyMetadata metodę RegisterReadOnly(String, Type, Type, PropertyMetadata) , zobaczysz, że każde Aquarium wystąpienie pobiera dwa elementy kolekcji. Dzieje się tak, ponieważ oba Fish wystąpienia są dodawane do tej samej listy, która jest współdzielona przez wszystkie wystąpienia klasy Aquarium. Dlatego, gdy intencja jest dla każdego wystąpienia obiektu, aby mieć własną listę, wartość domyślna powinna być ustawiona w konstruktorze klasy.

Inicjowanie kolekcji odczytu i zapisu

Poniższy przykład deklaruje właściwość zależności typu kolekcji do odczytu i zapisu w Aquarium klasie przy użyciu metod Register(String, Type, Type) sygnatur innych niż klucz i 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

Właściwości zależności FreezableCollection

Właściwość zależności typu kolekcji nie zgłasza automatycznie zmian w jej właściwościach podrzędnych. W związku z tym, jeśli wiążesz się z kolekcją, powiązanie może nie zgłaszać zmian, unieważniając niektóre scenariusze powiązania danych. Jeśli jednak używasz FreezableCollection<T> dla typu właściwości zależności, zmiany właściwości elementów kolekcji są prawidłowo zgłaszane, a powiązanie działa zgodnie z oczekiwaniami.

Aby włączyć powiązanie podwłaściwości w kolekcji obiektów zależności, użyj typu kolekcji , z ograniczeniem typu FreezableCollectiondowolnej DependencyObject klasy pochodnej.

Poniższy przykład deklaruje klasę Aquarium zawierającą FreezableCollection ograniczenie FrameworkElementtypu . Domyślna wartość kolekcji nie jest uwzględniona w przekazanej PropertyMetadata metodzie RegisterReadOnly(String, Type, Type, PropertyMetadata) , a zamiast tego konstruktor klasy jest używany do ustawiania domyślnej wartości kolekcji na nową 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

Zobacz też