集合类型附加属性

Collection-Type Attached Properties

我想通过收集 DependencyObjects 的附加收集类型 属性 来扩展 FrameworkElement 元素,例如 Button class。

困难在于对集合项的绑定不起作用:没有出现调试或运行时错误,但从未调用绑定的源代码。

我注意到附加的集合类型 属性 没有继承自 class DependencyObject

我猜 DataContext 属性 将被任何子 DependencyObject 对象继承(只要父对象也是 DependencyObject 对象)。由于附加的集合类型 属性 不从 DependencyObject 继承,因此不会发生 DataContext 属性 继承。

  1. 我想知道为什么 DependencyObject 的实例可以继承 DataContext 属性 因为 DataContextFrameworkElement 中定义的 属性? DependencyObject 如何管理 DataContext 查找?
  2. 为什么用 ElementName=PageName 指定绑定源也不起作用(例如 {Binding MyProperty="{Binding DataContext.PropertySource1, ElementName=PageName})?如果 DependencyObject 也负责 ElementName 查找,它是怎么做到的?
  3. 是否有继承DependencyObject的UWP合集? (在WPF中有FreezableCollection<T>class但是我在UWP环境下找不到挂件。)

下面的 XAML 标记显示了一个示例扩展,其中 Binding 不起作用。

<Button Name="Button">
    <ext:MyExtension.MyCollection>
        <ext:MyDependencyObject MyProperty="{Binding PropertySource1}"/>
        <ext:MyDependencyObject MyProperty="{Binding PropertySource1}"/> 
    </ext:MyExtension.MyCollection>
</Button>

如果我对非集合类型的附加属性执行以下扩展,则可以正确解析绑定。

<Button Name="Button">
    <ext:MyExtension.MyProperty>
        <ext:MyDependencyObject MyProperty="{Binding PropertySource1}"/>
    </ext:MyExtension.MyProperty>
</Button>

下面的代码显示了一个示例集合类型附加 属性 实现。考虑附加的 属性 class 还包含一个非集合类型附加的定义 属性(与绑定一起工作)。

public class MyDependencyObject: DependencyObject
{
    public object MyProperty
    {
        get { return (object)GetValue(MyPropertyProperty ); }
        set { SetValue(MyPropertyProperty , value); }
    }
    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty", typeof(object), typeof(MyProperty), null);
}

public class MyPropertyCollection : ObservableCollection<MyDependencyObject> { }

public static class MyExtension
{
    // Collection-type AttachedProperty with DependencyObject items

    public static MyPropertyCollection GetMyPropertyCollection(DependencyObject obj)
    {
        MyPropertyCollection collection = (MyPropertyCollection )obj.GetValue(MyCollectionProperty );
        if (collection == null)
        {
            collection = new MyPropertyCollection();

            collection.CollectionChanged +=
                (sender, e) =>
                {
                    //intiailization of elements possible
                };


            obj.SetValue(MappingsProperty, collection);
        }

        return collection;
    }

    public static void SetMyPropertyCollection(DependencyObject obj, MyPropertyCollection value)
    {
        obj.SetValue(MyCollectionProperty , value);
    }

    public static readonly DependencyProperty MyCollectionProperty =
        DependencyProperty.RegisterAttached("MyCollection", typeof(MyPropertyCollection), typeof(MyExtension), null);


    // DependencyObject-type AttachedProperty

    public static MyProperty GetMapping(DependencyObject obj)
    {
        return (MyProperty )obj.GetValue(MyPropertyProperty );
    }

    public static void SetMapping(DependencyObject obj, MyProperty value)
    {
        obj.SetValue(MyPropertyProperty , value);
    }

    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.RegisterAttached("MyProperty", typeof(MyDependencyObject), typeof(MyExtension), null);

}

在 WPF 中,可以通过使用 FreezableCollection<T> 或继承 Freezable class、IList<T>IList:

  1. DependencyObject 继承它,因为无法访问 DependencyObject 的两个必需方法,但是 Freezable 对它们进行了包装。
  2. 根据需要实施 IList<T>IList
  3. 向集合中添加新元素时调用 OnFreezablePropertyChanged(null, newItem)
  4. 从中删除元素时调用 OnFreezablePropertyChanged(item, null)

OnFreezablePropertyChanged方法内部会调用DependencyObject的两个方法来提供或移除继承上下文(source)。

但是在 UWP 中没有 Freezable 也没有这样的方法,所以在 Windows 10.0.10240.0 之前是不可能的。但是,如果您的目标是 v10.0.10240.0 或更高版本,您应该使用针对行为的 DependencyObjectCollection

The purpose of the DependencyObjectCollection class is mainly to support the tooling and portability of behaviors. Behaviors are a technique for defining certain basic interactions of a UI element entirely in XAML, without requiring an event handler and code-behind.

因此,在 UWP 和 WPF 中,仅当子项是 logical/visual 个子项或者它们是依赖项 属性 值时,才能向子项提供继承上下文。因此,在您的案例中使用 Binding 和 set ElementName 时不起作用。 ElementName 属性 用于在绑定附加到依赖对象而不是编译时在运行时解析对象。

UWP 绑定的工作方式类似于 WPF 绑定,因此例如请参阅第二个平台的 ElementObjectRef.GetObject