Bagikan melalui


Properti dependensi jenis koleksi (WPF .NET)

Artikel ini menyediakan panduan dan pola yang disarankan untuk menerapkan properti dependensi yang merupakan jenis koleksi.

Prasyarat

Artikel ini mengasumsikan pengetahuan dasar tentang properti dependensi, dan bahwa Anda telah membaca gambaran umum properti Dependensi. Untuk mengikuti contoh dalam artikel ini, ini membantu jika Anda terbiasa dengan Extensible Application Markup Language (XAML) dan tahu cara menulis aplikasi WPF.

Menerapkan properti dependensi jenis koleksi

Secara umum, pola implementasi untuk properti dependensi adalah pembungkus properti CLR yang DependencyProperty didukung oleh pengidentifikasi alih-alih bidang atau konstruksi lainnya. Anda dapat mengikuti pola yang sama saat menerapkan properti dependensi jenis koleksi. Polanya lebih kompleks jika jenis elemen koleksi adalah DependencyObject kelas turunan atau Freezable .

Menginisialisasi koleksi

Saat membuat properti dependensi, Anda biasanya menentukan nilai default melalui metadata properti dependensi alih-alih menentukan nilai properti awal. Namun, jika nilai properti Anda adalah jenis referensi, nilai default harus diatur dalam konstruktor kelas yang mendaftarkan properti dependensi. Metadata properti dependensi tidak boleh menyertakan nilai jenis referensi default karena nilai tersebut akan ditetapkan ke semua instans kelas, membuat kelas singleton.

Contoh berikut mendeklarasikan Aquarium kelas yang berisi kumpulan FrameworkElement elemen dalam generik List<T>. Nilai koleksi default tidak disertakan dalam yang diteruskan PropertyMetadata ke RegisterReadOnly(String, Type, Type, PropertyMetadata) metode , dan sebaliknya konstruktor kelas digunakan untuk mengatur nilai koleksi default ke generik Listbaru .

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

Kode pengujian berikut membuat instans terpisah Aquarium dan menambahkan item yang berbeda Fish ke setiap koleksi. Jika Anda menjalankan kode, Anda akan melihat bahwa setiap Aquarium instans memiliki satu item koleksi, seperti yang diharapkan.

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

Tetapi, jika Anda mengomentari konstruktor kelas dan meneruskan nilai koleksi default ke PropertyMetadata RegisterReadOnly(String, Type, Type, PropertyMetadata) metode , Anda akan melihat bahwa setiap Aquarium instans mendapatkan dua item koleksi! Ini karena kedua Fish instans ditambahkan ke daftar yang sama, yang dibagikan oleh semua instans kelas Akuarium. Jadi, ketika niatnya adalah agar setiap instans objek memiliki daftarnya sendiri, nilai default harus diatur dalam konstruktor kelas.

Menginisialisasi koleksi baca-tulis

Contoh berikut mendeklarasikan properti dependensi jenis koleksi baca-tulis di Aquarium kelas , menggunakan metode Register(String, Type, Type) tanda tangan non-kunci dan 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

Properti dependensi FreezableCollection

Properti dependensi jenis koleksi tidak secara otomatis melaporkan perubahan dalam subpropertinya. Akibatnya, jika Anda mengikat ke koleksi, pengikatan mungkin tidak melaporkan perubahan, membatalkan beberapa skenario pengikatan data. Tetapi, jika Anda menggunakan FreezableCollection<T> untuk jenis properti dependensi, perubahan pada properti elemen koleksi dilaporkan dengan benar dan pengikatan berfungsi seperti yang diharapkan.

Untuk mengaktifkan pengikatan subproperti dalam kumpulan objek dependensi, gunakan jenis FreezableCollectionkoleksi , dengan batasan jenis kelas turunan apa pun DependencyObject .

Contoh berikut mendeklarasikan Aquarium kelas yang berisi FreezableCollection dengan batasan FrameworkElementjenis . Nilai koleksi default tidak disertakan dalam yang diteruskan PropertyMetadata ke RegisterReadOnly(String, Type, Type, PropertyMetadata) metode , dan sebaliknya konstruktor kelas digunakan untuk mengatur nilai koleksi default ke yang baru 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

Lihat juga