因为我几乎找不到关于这个问题的文件,所以我花了一些时间在source code个左右,但这里有一个答案.
将依赖项属性注册为常规属性和附加属性(而不是"哲学"属性)之间存在差异(regular properties are intended to be used by the declaring type and its deriving types, attached properties are intended to be used as extensions on arbitrary DependencyObject
instances)."哲学的",因为正如@MarqueIV在对@ReedCopsey答案的 comments 中所注意到的,正则属性也可以用于任意DependencyObject
个实例.
此外,我不同意其他答案,即附加属性是"依赖属性类型",因为这是误导性的——没有任何"类型"的依赖属性.该框架并不关心该财产是否被登记为附属财产——甚至无法确定(从某种意义上说,这些信息没有被记录,因为它们无关紧要).事实上,所有属性都像附加属性一样进行注册,但对于常规属性,会执行一些额外的操作,稍微修改它们的行为.
为了省go 你自己查看源代码的麻烦,这里有一个简化版本.
注册未指定元数据的属性时,调用
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
调用时产生exactly the same个结果
DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
但是,在指定元数据时
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
相当于打电话
var property = DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
defaultMetadata: new PropertyMetadata
{
DefaultValue = "default value",
});
property.OverrideMetadata(
forType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
常规依赖项属性和附加依赖项属性之间的关键(也是唯一)区别是通过DependencyProperty.DefaultMetadata属性提供的默认元数据.这甚至在Remarks节中提到:
对于非附加属性,此属性返回的元数据类型不能强制转换为PropertyMetadata类型的派生类型,即使该属性最初是用派生元数据类型注册的.如果希望原始注册的元数据包括其原始可能派生的元数据类型,请调用GetMetadata(Type),将原始注册类型作为参数传递.
对于附加属性,此属性返回的元数据类型将与原始RegisterAttached注册方法中给出的类型匹配.
这在提供的代码中清晰可见.注册方法中也隐藏了一些提示,即对于RegisterAttached
,元数据参数名为defaultMetadata
,而对于Register
,元数据参数名为typeMetadata
.对于附加属性,提供的元数据将成为默认元数据.但是,对于常规属性,默认元数据始终是PropertyMetadata
的新实例,只设置了DefaultValue
(来自提供的元数据或自动设置).只有对OverrideMetadata
的后续调用实际使用了提供的元数据.
主要的实际区别是,对于常规属性,CoerceValueCallback
和PropertyChangedCallback
适用于only种类型,这些类型源自声明为所有者类型的类型,而对于附属属性,它们适用于all种类型.例如,在这种情况下:
var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");
如果该财产登记为附属财产,则登记为PropertyChangedCallback
will be called;如果该财产登记为普通财产,则登记为will not be called.CoerceValueCallback
也一样.
第二个区别源于这样一个事实:OverrideMetadata
要求提供的类型派生自DependencyObject
.实际上,这意味着常规属性的所有者类型可以是DependencyObject
中的must derive,而中附加属性的所有者类型可以是any(包括静态类、 struct 、枚举、委托等).
除了@MarqueIV的建议之外,我还在几个场合遇到过这样的观点,即常规属性和附加属性在XAML中的使用方式不同.也就是说,常规属性需要隐式名称语法,而不是附加属性所需的显式名称语法.从技术上讲,这是not true,尽管实际上通常是这样.为清楚起见,请执行以下操作:
<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" />
<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />
在pure XAML中,管理这些语法用法的唯一规则如下:
满足这些条件使您能够使用相应的语法,而不管支持依赖项属性是注册为常规属性还是附加属性.
现在,所提到的误解是由以下事实造成的:绝大多数教程(以及Visual Studio个代码片段)都指示您使用CLR属性作为常规依赖属性,而获取/设置附加属性的访问器.但是没有什么能阻止你同时使用这两种语言,允许你使用你喜欢的语法.