共用方式為


集合類型相依性屬性 (WPF .NET)

本文提供實作集合類型的相依性屬性的指引和建議模式。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

必要條件

本文假設您具備相依性屬性的基本知識,而且您已閱讀 相依性屬性概觀 。 若要遵循本文中的範例,如果您熟悉可延伸的應用程式標記語言(XAML),並知道如何撰寫 WPF 應用程式,它很有説明。

實作集合類型相依性屬性

一般而言,相依性屬性的實作模式是識別碼所 DependencyProperty 支援的 CLR 屬性包裝函式,而不是欄位或其他建構。 當您實作集合類型相依性屬性時,可以遵循相同的模式。 如果集合專案類型是 DependencyObjectFreezable 衍生類別,則模式會比較複雜。

初始化集合

當您建立相依性屬性時,通常會透過相依性屬性中繼資料指定預設值,而不是指定初始屬性值。 不過,如果您的屬性值是參考型別,則應該在註冊相依性屬性之類別的建構函式中設定預設值。 相依性屬性中繼資料不應該包含預設參考型別值,因為該值會指派給類別的所有實例,並建立單一類別。

下列範例會宣告類別 Aquarium ,其中包含泛型 List<T> 中的專案集合 FrameworkElement 。 傳遞至 RegisterReadOnly(String, Type, Type, PropertyMetadata) 方法時不會包含 PropertyMetadata 預設集合值,而是使用類別建構函式將預設集合值設定為新的泛型 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

下列測試程式碼會具現化兩個不同的 Aquarium 實例,並將不同的 Fish 專案加入至每個集合。 如果您執行程式碼,您會看到每個 Aquarium 實例都有單一集合專案,如預期般。

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

但是,如果您將類別建構函式批註化,並將預設集合值 PropertyMetadataRegisterReadOnly(String, Type, Type, PropertyMetadata) 傳遞給 方法,您會看到每個 Aquarium 實例取得兩個集合專案! 這是因為這兩 Fish 個實例都會新增至相同的清單,而此清單是由 Aquarium 類別的所有實例共用。 因此,當意圖是讓每個物件實例有自己的清單時,應該在類別建構函式中設定預設值。

初始化讀寫集合

下列範例會使用非索引鍵簽章方法和 Register(String, Type, Type)SetValue(DependencyProperty, Object) ,宣告 類別中的 Aquarium 讀寫集合類型相依性屬性。

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

FreezableCollection 相依性屬性

集合類型相依性屬性不會自動報告其子屬性中的變更。 因此,如果您要系結至集合,系結可能不會報告變更,使某些資料系結案例失效。 但是,如果您使用 FreezableCollection<T> 相依性屬性類型,則會正確報告集合專案屬性的變更,而且系結會如預期般運作。

若要在相依性物件的集合中啟用子屬性系結,請使用集合類型 FreezableCollection ,並搭配任何 DependencyObject 衍生類別的類型條件約束。

下列範例會宣告類別 Aquarium ,其中包含 FreezableCollection 型別條件約束為 的 FrameworkElement 。 傳遞至 方法時 PropertyMetadata 不會包含預設集合值,而是使用類別建構函式將預設集合值設定為新的 FreezableCollectionRegisterReadOnly(String, Type, Type, PropertyMetadata)

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

另請參閱