那么 RegisterAttached 中的 'ownerType' 到底是做什么用的? 这个问题一直困扰着我好几年,所以我终于仔细研究了 WindowsBase 中的 4.6.1 代码。
对于任何DependencyProperty,无论是否附加,最终归结为PropertyDescriptor WPF 为后期绑定XAML 访问获得什么类型,直到第一次才确定(在每个类型/property pairing based) 尝试进行此类访问。这种延迟是必要的,因为PropertyDescriptor 封装了绑定到特定类型的属性,而附加属性的目的是避免这种情况。
到发生 XAML 访问时,Register(...) 与 RegisterAttached(...) 的区别已经消失在(运行)时间的迷雾中。正如其他人在此页面上所指出的那样,“DependencyProperty”本身并没有编码附加与非变体之间的区别。假设每个 DP 都符合任一使用条件,仅受运行时可以计算出的内容的限制。
例如,下面的 .NET 代码似乎依赖 not 在 'tOwner' 类型上找到匹配的 instance 属性 作为允许附加访问的第一个要求。为了确认这一诊断,它会检查“tOwner”是否公开了其中一种静态访问方法。这是一个模糊的检查,因为它不验证方法签名。这些签名只对 XAML 访问很重要;附加属性的所有实际运行时目标必须是DependencyObjects,WPF 会尽可能通过DependencyObject.GetValue/SetValue 访问它。 (据报道,VS 设计器确实使用了静态访问器,如果没有它们,您的 XAML 将无法编译)
相关的.NET代码是静态函数DependencyPropertyDescriptor.FromProperty,这里用我自己的cmets展示(总结如下):
internal static DependencyPropertyDescriptor FromProperty(DependencyProperty dp, Type tOwner, Type tTarget, bool _)
{
/// 1. 'tOwner' must define a true CLR property, as obtained via reflection,
/// in order to obtain a normal (i.e. non-attached) DependencyProperty
if (tOwner.GetProperty(dp.Name) != null)
{
DependencyPropertyDescriptor dpd;
var dict = descriptor_cache;
lock (dict)
if (dict.TryGetValue(dp, out dpd))
return dpd;
dpd = new DependencyPropertyDescriptor(null, dp.Name, tTarget, dp, false);
lock (dict)
dict[dp] = dpd;
/// 2. Exiting here means that, if instance properties are defined on tOwner,
/// you will *never* get the attached property descriptor. Furthermore,
/// static Get/Set accessors, if any, will be ignored in favor of those instance
/// accessors, even when calling 'RegisterAttached'
return dpd;
}
/// 3. To obtain an attached DependencyProperty, 'tOwner' must define a public,
/// static 'get' or 'set' accessor (or both).
if ((tOwner.GetMethod("Get" + dp.Name) == null) && (tOwner.GetMethod("Set" + dp.Name) == null))
return null;
/// 4. If we are able to get a descriptor for the attached property, it is a
/// DependencyObjectPropertyDescriptor. This type and DependencyPropertyDescriptor
/// both derive directly from ComponentModel.PropertyDescriptor so they share
/// no 'is-a' relation.
var dopd = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, tTarget);
/// 5. Note: If the this line returns null, FromProperty isn't called below (new C# syntax)
/// 6. FromProperty() uses the distinction between descriptor types mentioned in (4.)
/// to configure 'IsAttached' on the returned DependencyProperty, so success here is
/// the only way attached property operations can succeed.
return dopd?.FromProperty(dopd);
}
总结:当调用RegisterAttached 来创建附加的DependencyProperty 时,'ownerType' 的唯一用途是识别定义适当静态 Get/Set 访问器的类型。这些访问器中至少有一个必须存在于“ownerType”上,否则 XAML 附加访问将不会编译或静默失败。虽然 RegisterAttached 在这种情况下没有失败,而是成功返回了一个失效的 DP。对于仅用于附加使用的 DP,“tOwner”不必从 DependencyObject 派生。您可以使用任何常规的 .NET 类,无论是静态的还是非静态的,实际上甚至可以是结构体!