集合类型依赖项属性

更新:2007 年 11 月

本主题为属性类型是集合类型的情况下如何实现依赖项属性提供了指导和建议的模式。

本主题包括下列各节。

  • 实现集合类型依赖项属性
  • 不使用默认值初始化集合
  • 从集合属性报告绑定值更改
  • 相关主题

实现集合类型依赖项属性

通常情况下对于依赖项属性,遵循的实现是定义一个 CLR 属性包装,其中该属性由一个 DependencyProperty 标识符(而不是字段或其他构造)支持。若要实现一个集合类型属性,请遵循同样的模式。但是,每当集合中包含的类型本身为 DependencyObjectFreezable 派生类时,集合类型属性会使模式更加复杂。

不使用默认值初始化集合

在您创建依赖项属性时,没有将属性默认值指定为初始字段值,而是通过依赖项属性元数据指定默认值。如果属性是一种引用类型,则在依赖项属性元数据中指定的默认值不是各个实例的默认值,而是应用到该类型的所有实例的默认值。因此,您必须注意不要将集合属性元数据所定义的单个静态集合用作新创建的类型实例的适用默认值。并且,在类构造函数逻辑中,必须确保特意将集合值设置为唯一的(实例)集合。否则,会创建一个意外的单独的类。

请看下面的示例。下面的示例部分演示 Aquarium 类的定义。该类定义集合类型依赖项属性 AquariumObjects,它将泛型 List<T> 类型与 FrameworkElement 类型约束一起使用。在对依赖项属性的 Register(String, Type, Type, PropertyMetadata) 调用中,元数据将默认值建立为一个新的泛型 List<T>

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


...


}

但是,如果您只是使用下面的代码,则该单个列表默认值会在 Aquarium 的所有实例中共享。如果运行下面的测试代码(该测试代码旨在演示如何实例化两个独立的 Aquarium 实例并向这两个实例中分别添加一个不同的 Fish),您会看到令人惊讶的结果:

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

每个集合的计数不是 1,而是 2! 这是因为元数据中的单个构造函数调用导致每个 Aquarium 都将其 Fish 添加到默认值集合,从而会在所有实例中共享。这种情况应该不是您想要的。

为了纠正此问题,在类构造函数调用中,必须将集合依赖项属性值重设为唯一的实例。因为该属性是只读的依赖项属性,所以应使用 SetValue(DependencyPropertyKey, Object) 方法通过只能在该类中访问的 DependencyPropertyKey 对该属性进行设置。

public Aquarium() : base()
{
    SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>()); 
}

现在,如果您再次运行上述测试代码,应能看到更接近预期的结果,其中每个 Aquarium 都支持它自己的唯一集合。

如果您选择使集合属性成为读写属性,则此模式会稍有变化。此时,您将从构造函数调用公共 set 访问器来执行初始化,即仍使用公共的 DependencyProperty 标识符来调用 set 包装中 SetValue(DependencyProperty, Object) 的非键签名。

从集合属性报告绑定值更改

本身为依赖项属性的集合属性不会自动报告对其子属性的更改。如果正在创建与集合的绑定,这可以防止绑定报告更改而使某些数据绑定方案无效。但是,如果您使用集合类型 FreezableCollection<T> 作为您的集合类型,则会以合适的方式报告对集合所包含元素的子属性更改,并且绑定会按预期工作。

若要在依赖项对象集合中启用子属性绑定,请将集合属性创建为类型 FreezableCollection<T>,该集合的类型约束到任何 DependencyObject 派生类。

请参见

概念

XAML 和自定义类

数据绑定概述

依赖项属性概述

自定义的依赖项属性

依赖项属性元数据

参考

FreezableCollection<T>