WPF 中的依赖属性和附加属性有什么区别?
WPF 中的(自定义)依赖属性和附加属性之间有什么区别? 各有什么用途? 这些实现通常有何不同?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
摘要
由于我几乎没有找到有关此事的文档,因此需要对
将依赖属性注册为常规属性和附加属性之间存在区别,除了“哲学”属性之外(常规属性旨在由声明类型及其派生类型使用,附加属性旨在由声明类型及其派生类型使用)用作任意
DependencyObject
实例的扩展)。 “哲学”,因为正如 @MarqueIV 在对 @ReedCopsey 的答案的评论中注意到的那样,常规属性也可以与任意DependencyObject
实例一起使用。此外,我不同意其他答案,指出附加属性是“依赖属性的类型”,因为它具有误导性 - 不存在任何依赖属性的“类型”。 框架不关心该属性是否被注册为附加的 - 甚至无法确定(从某种意义上说,该信息没有被记录,因为它是不相关的)。 事实上,所有属性都被注册为就好像它们是附加属性一样,但是对于常规属性,会执行一些额外的操作来稍微修改它们的行为。
代码摘录
为了让您免去亲自查看源代码的麻烦,下面是所发生情况的精简版本。
在未指定元数据的情况下注册属性时,调用
会产生与调用完全相同的结果。
但是,在指定元数据时,调用
等效于调用
结论
常规依赖项属性和附加依赖项属性之间的关键(也是唯一)区别是通过 DependencyProperty.DefaultMetadata 属性。 这甚至在 备注部分:
这在提供的代码中清晰可见。 注册方法中还隐藏了一些小提示,即对于
RegisterAttached
,元数据参数名为defaultMetadata
,而对于Register
,它名为类型元数据
。 对于附加属性,提供的元数据将成为默认元数据。 但是,对于常规属性,默认元数据始终是仅设置了DefaultValue
的PropertyMetadata
的新实例(来自提供的元数据或自动设置)。 只有对OverrideMetadata
的后续调用才实际使用提供的元数据。主要的实际区别
在于,对于常规属性,
CoerceValueCallback
和PropertyChangedCallback
仅适用于从声明为所有者的类型派生的类型类型,对于附加属性,它们适用于所有类型。 例如,在这种情况下:如果该属性注册为附加属性,则注册的
PropertyChangedCallback
将被调用,但如果它是附加属性,则不会被调用注册为普通财产。CoerceValueCallback
也是如此。第二个区别源于以下事实:
OverrideMetadata
要求提供的类型源自DependencyObject
。 实际上,这意味着常规属性的所有者类型必须派生自DependencyObject
,而附加属性的所有者类型可以是任何类型(包括静态类) 、结构、枚举、委托等)。补充
除了 @MarqueIV 的建议之外,我还多次遇到这样的观点:常规属性和附加属性在 XAML 中的使用方式有所不同。 即,常规属性需要隐式名称语法,而不是附加属性所需的显式名称语法。 从技术上讲,这不正确,尽管实际上通常是这样。 为了清楚起见:
在纯 XAML 中,管理这些语法的使用的唯一规则如下:
满足这些条件使您能够使用相应的语法,无论支持依赖属性是注册为常规属性还是附加属性。
现在提到的误解是由于绝大多数教程(以及现有的 Visual Studio 代码片段)指示您使用 CLR 属性作为常规依赖项属性,并获得/设置附加访问器。 但是没有什么可以阻止您同时使用两者,从而允许您使用您喜欢的任何语法。
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 arbitraryDependencyObject
instances.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
yields exactly the same result as calling
However, when specifying metadata, calling
is equivalent to calling
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:
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 nameddefaultMetadata
, whereas forRegister
it is namedtypeMetadata
. For attached properties the provided metadata becomes the default metadata. In case of regular properties however, the default metadata is always a fresh instance ofPropertyMetadata
with onlyDefaultValue
set (either from provided metadata or automatically). Only the subsequent call toOverrideMetadata
actually uses the provided metadata.Consequences
The main practical difference is that in case of regular properties the
CoerceValueCallback
andPropertyChangedCallback
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: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 toCoerceValueCallback
.A secondary difference stems from the fact that
OverrideMetadata
requires that supplied type derives fromDependencyObject
. In practice it means that the owner type for regular properties must derive fromDependencyObject
, whereas for attached properties in can be any type (including static classes, structs, enums, delegates, etc.).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:
In pure XAML, the only rules governing the usage of these syntaxes are the following:
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.
附加属性基本上是针对容器元素的。例如,如果您有一个网格并且有 grid.row,那么现在这被认为是网格元素的附加属性。您也可以在 texbox、button 等中使用此属性来设置其放置在网格中。
依赖属性就像该属性基本上属于某个其他类并在其他类中使用。
例如:就像你有一个矩形
这里的 height 和 width 是矩形的常规属性,但 left 和 top 是依赖属性,因为它属于 Canvass 类。
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.
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.
附加属性是一种特殊的 DependencyProperties。 它们允许您将一个值附加到一个对该值一无所知的对象。
这个概念的一个很好的例子是布局面板。 每个布局面板需要不同的数据来对齐其子元素。 Canvas 需要 Top 和 Left,DockPanel 需要 Dock 等。由于您可以编写自己的布局面板,因此列表是无限的。 所以您会看到,不可能在所有 WPF 控件上都拥有所有这些属性。
解决方案是附加属性。 它们是由需要特定上下文中另一个控件的数据的控件定义的。 例如,由父布局面板对齐的元素。
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.
我认为您可以在类本身中定义附加属性,也可以在另一个类中定义它。 我们总是可以使用附加属性来扩展标准的微软控件。 但是依赖属性,您可以在自己的自定义控件中定义它。 例如,您可以从标准控件继承您的控件,并在您自己的控件中定义依赖属性并使用它。 这相当于定义一个附加属性,并在标准控件中使用这个附加属性。
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.
附加属性是一种依赖属性。 区别在于它们的使用方式。
对于附加属性,该属性是在与使用该属性的类不同的类上定义的。 这通常用于布局。 很好的例子是Panel.ZIndex 或Grid.Row - 您将其应用于控件(即:按钮),但它实际上是在Panel 或Grid 中定义的。 该属性“附加”到按钮的实例。
例如,这允许容器创建可在任何 UI 元素上使用的属性。
至于实现差异 - 基本上只是定义属性时使用 Register 与 RegisterAttached 的问题。
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.
This allows a container, for example, to create properties that can be used on any UIelement.
As for implementation differences - it's basically just a matter of using Register vs. RegisterAttached when you define the property.