What's the difference between a (custom) dependency property and an attached property in WPF? What are the uses for each? How do the implementations typically differ?
在WPF中(自定义)依赖属性和附加属性之间的区别是什么?它们的用途是什么?这些实现通常有何不同?
5 个解决方案
#1
65
Attached properties are a type of dependency property. The difference is in how they're used.
附加属性是一种依赖属性。不同之处在于它们的使用方式。
With an attached property, the property is defined on a class that isn't the same class for which it's being used. This is usually used for layout. Good examples are Panel.ZIndex or Grid.Row - you apply this to a control (ie: Button), but it's actually defined in Panel or Grid. The property is "attached" to the button's instance.
对于附加属性,属性定义在与它所使用的类不同的类上。这通常用于布局。很好的例子是面板。ZIndex或网格。行——你把这个应用到控件(例如:按钮),但是它实际上是在面板或网格中定义的。属性“附加”到按钮的实例。
This allows a container, for example, to create properties that can be used on any UIelement.
例如,这允许容器创建可以在任何UIelement上使用的属性。
As for implementation differences - it's basically just a matter of using Register vs. RegisterAttached when you define the property.
至于实现差异——在定义属性时,基本上只需要使用Register或registerattach即可。
#2
5
Attached properties are basically meant for the container elements.like if you have a grid and you have grid.row now this is considered to be an attached property of a grid element.also you can use this property in texbox,button etc to set its place in the grid.
附加属性主要用于容器元素。比如你有一个网格,你有一个网格。行现在被认为是网格元素的附加属性。您还可以在texbox、button等中使用此属性设置其在网格中的位置。
Dependency property is like the property basically belongs to some other class and is used in other class. eg: like you have a rectangle here height and width are regular properties of rectangle,but left and top are the dependency property as it belongs to Canvass class.
依赖属性就像属性基本上属于其他类,并在其他类中使用。就像你有一个矩形一样,高和宽是矩形的常规属性,但左和上是依赖属性,因为它属于Canvass类。
#3
0
I think you can defined attached property in the class itself or you can define it in another class. We always could use attached property to extend standard microsoft controls. But dependency property, you define it in your own custom control. e.g. You can inherit your control from a standard control, and define a dependency property in your own control and use it. This is equivalent to define an attached property, and use this attached property in the standard control.
我认为你可以在类本身中定义附加属性或者在另一个类中定义它。我们总是可以使用附加属性来扩展微软的标准控件。但是依赖属性,你在你自己的自定义控件中定义它。例:您可以从一个标准控件继承您的控件,并在您自己的控件中定义一个依赖属性并使用它。这相当于定义一个附加属性,并在标准控件中使用这个附加属性。
#4
0
Abstract
Since I found little to no documentation on the matter, it took some poking around the source code, but here's an answer.
由于我几乎没有找到任何关于这个问题的文档,所以需要对源代码进行一些研究,但是这里有一个答案。
There is a difference between registering a dependency property as a regular and as an attached property, other than a "philosophical" one (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). "Philosophical", because, as @MarqueIV noticed in his comment to @ReedCopsey's answer, regular properties can also be used with arbitrary DependencyObject
instances.
将依赖项属性注册为正则属性和作为附加属性之间存在差异,而不是“哲学”属性(规则属性用于声明类型和它的派生类型,附加属性用于在任意的DependencyObject实例上作为扩展)。“哲学的”,因为正如@MarqueIV在对@ReedCopsey的回答的评论中所注意到的,正则属性也可以用于任意的DependencyObject实例。
Moreover, I have to disagree with other answers stating that attached property is "type of dependency property", because it's misleading - there aren't any "types" of dependency properties. The framework doesn't care if the property was registered as attached or not - it's not even possible to determine (in the sense that this information is not recorded, because it's irrelevant). In fact, all properties are registered as if they were attached properties, but in case of regular ones some additional things are done that slightly modify their behavior.
此外,我不得不反对其他的说法,即附加属性是“依赖属性的类型”,因为它具有误导性——没有任何依赖属性的“类型”。框架并不关心属性是否被注册为附加属性——它甚至不可能确定(因为这个信息没有被记录,因为它是无关的)。事实上,所有属性都被注册为附加属性,但对于常规属性,会做一些额外的事情来稍微修改它们的行为。
Code excerpt
To save you the trouble of going through the source code yourself, here's a boiled down version of what happens.
为了避免您自己检查源代码的麻烦,这里有一个简化版本。
When registering a property without metadata specified, calling
当注册没有指定元数据的属性时,调用
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
yields exactly the same result as calling
产生与调用完全相同的结果
DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
However, when specifying metadata, calling
但是,在指定元数据时,调用。
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
is equivalent to calling
相当于调用
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
});
Conclusions
The key (and only) difference between regular and attached dependency properties is the default metadata available through DependencyProperty.DefaultMetadata property. This is even mentioned in the Remarks section:
常规依赖属性和附加依赖属性之间的关键(也是唯一的)区别是通过DependencyProperty提供的默认元数据。DefaultMetadata财产。这一点甚至在评论一节中提到:
For nonattached properties, the metadata type returned by this property cannot be cast to derived types of PropertyMetadata type, even if the property was originally registered with a derived metadata type. If you want the originally registered metadata including its original possibly derived metadata type, call GetMetadata(Type) instead, passing the original registering type as a parameter.
对于非附加属性,此属性返回的元数据类型不能转换为PropertyMetadata类型的派生类型,即使该属性最初注册为派生元数据类型。如果您想要原始注册的元数据(包括原始的可能派生的元数据类型),可以调用GetMetadata(类型),将原始注册类型作为参数传递。
For attached properties, the type of the metadata returned by this property will match the type given in the original RegisterAttached registration method.
对于附加属性,此属性返回的元数据的类型将匹配原始注册附加注册方法中给定的类型。
This is clearly visible in the provided code. Little hints are also hidden in the registering methods, i.e. for RegisterAttached
the metadata parameter is named defaultMetadata
, whereas for Register
it is named typeMetadata
. For attached properties the provided metadata becomes the default metadata. In case of regular properties however, the default metadata is always a fresh instance of PropertyMetadata
with only DefaultValue
set (either from provided metadata or automatically). Only the subsequent call to OverrideMetadata
actually uses the provided metadata.
这在提供的代码中是明显可见的。注册方法中也隐藏了一些提示,例如,对于registerattach,元数据参数被命名为defaultMetadata,而对于Register,它被命名为typeMetadata。对于附加属性,提供的元数据成为默认元数据。但是,对于常规属性,默认元数据始终是只有DefaultValue设置的PropertyMetadata的一个新实例(要么来自提供的元数据,要么是自动设置的)。只有对OverrideMetadata的后续调用才真正使用所提供的元数据。
Consequences
The main practical difference is that in case of regular properties the CoerceValueCallback
and PropertyChangedCallback
are applicable only for types derived from the type declared as the owner type, and for attached properties they're applicable for all types. E.g. in this scenario:
主要的实际区别是,在常规属性的情况下,强制评估和PropertyChangedCallback只适用于从声明为所有者类型的类型派生的类型,以及它们适用于所有类型的附加属性。例如,在这种情况下:
var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");
the registered PropertyChangedCallback
will be called if the property was registered as an attached property, but will not be called if it was registered as a regular property. Same goes to CoerceValueCallback
.
如果将该属性注册为附加属性,则将调用已注册的PropertyChangedCallback,但如果将其注册为常规属性,则不会调用该属性。→CoerceValueCallback相同。
A secondary difference stems from the fact that OverrideMetadata
requires that supplied type derives from DependencyObject
. In practice it means that the owner type for regular properties must derive from DependencyObject
, whereas for attached properties in can be any type (including static classes, structs, enums, delegates, etc.).
第二个差异源于OverrideMetadata要求提供的类型派生自DependencyObject。在实践中,它意味着常规属性的所有者类型必须派生自DependencyObject,而对于包含在其中的附加属性可以是任何类型(包括静态类、结构、枚举、委托等等)。
Supplement
Besides @MarqueIV's suggestion, on several occasions I've come across opinions that regular and attached properties differ in the way they can be used in XAML. Namely, that regular properties require implicit name syntax as opposed to explicit name syntax required by attached properties. This is technically not true, although in practice it usually is the case. For clarity:
除了@MarqueIV的建议之外,我也遇到过一些意见,即常规属性和附加属性在XAML中的使用方式不同。也就是说,常规属性需要隐式名称语法,而不是附加属性所需的显式名称语法。这在技术上是不正确的,尽管在实践中通常是这样的。为了清晰:
<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" />
<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />
In pure XAML, the only rules governing the usage of these syntaxes are the following:
在纯粹的XAML中,管理这些语法使用的唯一规则是:
- Implicit name syntax can be used on an element if and only if the class that this element represents has a CLR property of that name
- 当且仅当该元素表示的类具有该名称的CLR属性时,可以在元素上使用隐式名称语法
- Explicit name syntax can be used on an element if and only if the class specified by the first part of the full name exposes appropriate static get/set methods (referred to as accessors) with names matching the second part of the full name
- 当且仅当全名第一部分指定的类公开适当的静态get/set方法(称为访问器)、名称与全名第二部分匹配时,可以在元素上使用显式名称语法
Satisfying these conditions enables you to use corresponding syntax regardless of whether the backing dependency property was registered as regular or attached.
满足这些条件后,您就可以使用相应的语法,而不必考虑支持依赖项属性是注册为常规属性还是附加属性。
Now the mentioned misconception is caused by the fact that vast majority of tutorials (together with stock Visual Studio code snippets) instruct you to use CLR property for regular dependency properties, and get/set accessors for attached ones. But there's nothing stopping you from using both at the same time, allowing you to use whichever syntax you prefer.
现在,所提到的误解是由于绝大多数教程(连同stock Visual Studio代码片段)指导您将CLR属性用于常规依赖属性,并为附加的属性获取/设置访问器。但是没有什么能阻止您同时使用这两种语法,允许您使用您喜欢的任何语法。
#5
-2
Attached properties are a special kind of DependencyProperties. They allow you to attach a value to an object that does not know anything about this value. A good example for this concept are layout panels. Each layout panel needs different data to align its child elements. The Canvas needs Top and Left, The DockPanel needs Dock, etc. Since you can write your own layout panel, the list is infinite. So you see, it's not possible to have all those properties on all WPF controls. The solution are attached properties. They are defined by the control that needs the data from another control in a specific context. For example an element that is aligned by a parent layout panel.
附加属性是一种特殊的依赖属性。它们允许您将一个值附加到一个不知道该值的对象。这个概念的一个很好的例子是布局面板。每个布局面板需要不同的数据来对齐它的子元素。画布需要顶部和左侧,DockPanel需要Dock等,因为您可以编写自己的布局面板,所以列表是无限的。你看,不可能在所有WPF控件上都有这些属性。解是附加性质。它们由需要来自特定上下文中另一个控件的数据的控件定义。例如,一个由父布局面板对齐的元素。
#1
65
Attached properties are a type of dependency property. The difference is in how they're used.
附加属性是一种依赖属性。不同之处在于它们的使用方式。
With an attached property, the property is defined on a class that isn't the same class for which it's being used. This is usually used for layout. Good examples are Panel.ZIndex or Grid.Row - you apply this to a control (ie: Button), but it's actually defined in Panel or Grid. The property is "attached" to the button's instance.
对于附加属性,属性定义在与它所使用的类不同的类上。这通常用于布局。很好的例子是面板。ZIndex或网格。行——你把这个应用到控件(例如:按钮),但是它实际上是在面板或网格中定义的。属性“附加”到按钮的实例。
This allows a container, for example, to create properties that can be used on any UIelement.
例如,这允许容器创建可以在任何UIelement上使用的属性。
As for implementation differences - it's basically just a matter of using Register vs. RegisterAttached when you define the property.
至于实现差异——在定义属性时,基本上只需要使用Register或registerattach即可。
#2
5
Attached properties are basically meant for the container elements.like if you have a grid and you have grid.row now this is considered to be an attached property of a grid element.also you can use this property in texbox,button etc to set its place in the grid.
附加属性主要用于容器元素。比如你有一个网格,你有一个网格。行现在被认为是网格元素的附加属性。您还可以在texbox、button等中使用此属性设置其在网格中的位置。
Dependency property is like the property basically belongs to some other class and is used in other class. eg: like you have a rectangle here height and width are regular properties of rectangle,but left and top are the dependency property as it belongs to Canvass class.
依赖属性就像属性基本上属于其他类,并在其他类中使用。就像你有一个矩形一样,高和宽是矩形的常规属性,但左和上是依赖属性,因为它属于Canvass类。
#3
0
I think you can defined attached property in the class itself or you can define it in another class. We always could use attached property to extend standard microsoft controls. But dependency property, you define it in your own custom control. e.g. You can inherit your control from a standard control, and define a dependency property in your own control and use it. This is equivalent to define an attached property, and use this attached property in the standard control.
我认为你可以在类本身中定义附加属性或者在另一个类中定义它。我们总是可以使用附加属性来扩展微软的标准控件。但是依赖属性,你在你自己的自定义控件中定义它。例:您可以从一个标准控件继承您的控件,并在您自己的控件中定义一个依赖属性并使用它。这相当于定义一个附加属性,并在标准控件中使用这个附加属性。
#4
0
Abstract
Since I found little to no documentation on the matter, it took some poking around the source code, but here's an answer.
由于我几乎没有找到任何关于这个问题的文档,所以需要对源代码进行一些研究,但是这里有一个答案。
There is a difference between registering a dependency property as a regular and as an attached property, other than a "philosophical" one (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). "Philosophical", because, as @MarqueIV noticed in his comment to @ReedCopsey's answer, regular properties can also be used with arbitrary DependencyObject
instances.
将依赖项属性注册为正则属性和作为附加属性之间存在差异,而不是“哲学”属性(规则属性用于声明类型和它的派生类型,附加属性用于在任意的DependencyObject实例上作为扩展)。“哲学的”,因为正如@MarqueIV在对@ReedCopsey的回答的评论中所注意到的,正则属性也可以用于任意的DependencyObject实例。
Moreover, I have to disagree with other answers stating that attached property is "type of dependency property", because it's misleading - there aren't any "types" of dependency properties. The framework doesn't care if the property was registered as attached or not - it's not even possible to determine (in the sense that this information is not recorded, because it's irrelevant). In fact, all properties are registered as if they were attached properties, but in case of regular ones some additional things are done that slightly modify their behavior.
此外,我不得不反对其他的说法,即附加属性是“依赖属性的类型”,因为它具有误导性——没有任何依赖属性的“类型”。框架并不关心属性是否被注册为附加属性——它甚至不可能确定(因为这个信息没有被记录,因为它是无关的)。事实上,所有属性都被注册为附加属性,但对于常规属性,会做一些额外的事情来稍微修改它们的行为。
Code excerpt
To save you the trouble of going through the source code yourself, here's a boiled down version of what happens.
为了避免您自己检查源代码的麻烦,这里有一个简化版本。
When registering a property without metadata specified, calling
当注册没有指定元数据的属性时,调用
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
yields exactly the same result as calling
产生与调用完全相同的结果
DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
However, when specifying metadata, calling
但是,在指定元数据时,调用。
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
is equivalent to calling
相当于调用
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
});
Conclusions
The key (and only) difference between regular and attached dependency properties is the default metadata available through DependencyProperty.DefaultMetadata property. This is even mentioned in the Remarks section:
常规依赖属性和附加依赖属性之间的关键(也是唯一的)区别是通过DependencyProperty提供的默认元数据。DefaultMetadata财产。这一点甚至在评论一节中提到:
For nonattached properties, the metadata type returned by this property cannot be cast to derived types of PropertyMetadata type, even if the property was originally registered with a derived metadata type. If you want the originally registered metadata including its original possibly derived metadata type, call GetMetadata(Type) instead, passing the original registering type as a parameter.
对于非附加属性,此属性返回的元数据类型不能转换为PropertyMetadata类型的派生类型,即使该属性最初注册为派生元数据类型。如果您想要原始注册的元数据(包括原始的可能派生的元数据类型),可以调用GetMetadata(类型),将原始注册类型作为参数传递。
For attached properties, the type of the metadata returned by this property will match the type given in the original RegisterAttached registration method.
对于附加属性,此属性返回的元数据的类型将匹配原始注册附加注册方法中给定的类型。
This is clearly visible in the provided code. Little hints are also hidden in the registering methods, i.e. for RegisterAttached
the metadata parameter is named defaultMetadata
, whereas for Register
it is named typeMetadata
. For attached properties the provided metadata becomes the default metadata. In case of regular properties however, the default metadata is always a fresh instance of PropertyMetadata
with only DefaultValue
set (either from provided metadata or automatically). Only the subsequent call to OverrideMetadata
actually uses the provided metadata.
这在提供的代码中是明显可见的。注册方法中也隐藏了一些提示,例如,对于registerattach,元数据参数被命名为defaultMetadata,而对于Register,它被命名为typeMetadata。对于附加属性,提供的元数据成为默认元数据。但是,对于常规属性,默认元数据始终是只有DefaultValue设置的PropertyMetadata的一个新实例(要么来自提供的元数据,要么是自动设置的)。只有对OverrideMetadata的后续调用才真正使用所提供的元数据。
Consequences
The main practical difference is that in case of regular properties the CoerceValueCallback
and PropertyChangedCallback
are applicable only for types derived from the type declared as the owner type, and for attached properties they're applicable for all types. E.g. in this scenario:
主要的实际区别是,在常规属性的情况下,强制评估和PropertyChangedCallback只适用于从声明为所有者类型的类型派生的类型,以及它们适用于所有类型的附加属性。例如,在这种情况下:
var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");
the registered PropertyChangedCallback
will be called if the property was registered as an attached property, but will not be called if it was registered as a regular property. Same goes to CoerceValueCallback
.
如果将该属性注册为附加属性,则将调用已注册的PropertyChangedCallback,但如果将其注册为常规属性,则不会调用该属性。→CoerceValueCallback相同。
A secondary difference stems from the fact that OverrideMetadata
requires that supplied type derives from DependencyObject
. In practice it means that the owner type for regular properties must derive from DependencyObject
, whereas for attached properties in can be any type (including static classes, structs, enums, delegates, etc.).
第二个差异源于OverrideMetadata要求提供的类型派生自DependencyObject。在实践中,它意味着常规属性的所有者类型必须派生自DependencyObject,而对于包含在其中的附加属性可以是任何类型(包括静态类、结构、枚举、委托等等)。
Supplement
Besides @MarqueIV's suggestion, on several occasions I've come across opinions that regular and attached properties differ in the way they can be used in XAML. Namely, that regular properties require implicit name syntax as opposed to explicit name syntax required by attached properties. This is technically not true, although in practice it usually is the case. For clarity:
除了@MarqueIV的建议之外,我也遇到过一些意见,即常规属性和附加属性在XAML中的使用方式不同。也就是说,常规属性需要隐式名称语法,而不是附加属性所需的显式名称语法。这在技术上是不正确的,尽管在实践中通常是这样的。为了清晰:
<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" />
<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />
In pure XAML, the only rules governing the usage of these syntaxes are the following:
在纯粹的XAML中,管理这些语法使用的唯一规则是:
- Implicit name syntax can be used on an element if and only if the class that this element represents has a CLR property of that name
- 当且仅当该元素表示的类具有该名称的CLR属性时,可以在元素上使用隐式名称语法
- Explicit name syntax can be used on an element if and only if the class specified by the first part of the full name exposes appropriate static get/set methods (referred to as accessors) with names matching the second part of the full name
- 当且仅当全名第一部分指定的类公开适当的静态get/set方法(称为访问器)、名称与全名第二部分匹配时,可以在元素上使用显式名称语法
Satisfying these conditions enables you to use corresponding syntax regardless of whether the backing dependency property was registered as regular or attached.
满足这些条件后,您就可以使用相应的语法,而不必考虑支持依赖项属性是注册为常规属性还是附加属性。
Now the mentioned misconception is caused by the fact that vast majority of tutorials (together with stock Visual Studio code snippets) instruct you to use CLR property for regular dependency properties, and get/set accessors for attached ones. But there's nothing stopping you from using both at the same time, allowing you to use whichever syntax you prefer.
现在,所提到的误解是由于绝大多数教程(连同stock Visual Studio代码片段)指导您将CLR属性用于常规依赖属性,并为附加的属性获取/设置访问器。但是没有什么能阻止您同时使用这两种语法,允许您使用您喜欢的任何语法。
#5
-2
Attached properties are a special kind of DependencyProperties. They allow you to attach a value to an object that does not know anything about this value. A good example for this concept are layout panels. Each layout panel needs different data to align its child elements. The Canvas needs Top and Left, The DockPanel needs Dock, etc. Since you can write your own layout panel, the list is infinite. So you see, it's not possible to have all those properties on all WPF controls. The solution are attached properties. They are defined by the control that needs the data from another control in a specific context. For example an element that is aligned by a parent layout panel.
附加属性是一种特殊的依赖属性。它们允许您将一个值附加到一个不知道该值的对象。这个概念的一个很好的例子是布局面板。每个布局面板需要不同的数据来对齐它的子元素。画布需要顶部和左侧,DockPanel需要Dock等,因为您可以编写自己的布局面板,所以列表是无限的。你看,不可能在所有WPF控件上都有这些属性。解是附加性质。它们由需要来自特定上下文中另一个控件的数据的控件定义。例如,一个由父布局面板对齐的元素。