.net WPF 中的依赖属性和附加属性有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1240699/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 13:07:19  来源:igfitidea点击:

What's the difference between a dependency property and an attached property in WPF?

.netwpfdependency-propertiesattached-properties

提问by kenwarner

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 中的(自定义)依赖属性和附加属性有什么区别?各有什么用途?实现通常有何不同?

采纳答案by Reed Copsey

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.

使用附加属性,该属性定义在一个与使用它的类不同的类上。这通常用于布局。很好的例子是 Panel.ZIndex 或 Grid.Row - 您将其应用于控件(即:按钮),但它实际上是在 Panel 或 Grid 中定义的。该属性“附加”到按钮的实例。

This allows a container, for example, to create properties that can be used on any UIelement.

例如,这允许容器创建可用于任何 UI 元素的属性。

As for implementation differences - it's basically just a matter of using Register vs. RegisterAttached when you define the property.

至于实现差异 - 基本上只是在定义属性时使用 Register 与 RegisterAttached 的问题。

回答by Grx70

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 arbitraryDependencyObjectinstances). "Philosophical", because, as @MarqueIV noticed in his comment to @ReedCopsey's answer, regular properties can also be used with arbitrary DependencyObjectinstances.

将依赖属性注册为常规属性和附加属性之间存在差异,而不是“哲学”属性常规属性旨在由声明类型及其派生类型使用,附加属性旨在用作任意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 sameresult 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.DefaultMetadataproperty. This is even mentioned in the Remarkssection:

常规和附加依赖项属性之间的主要(也是唯一)区别是通过DependencyProperty.DefaultMetadata属性提供的默认元数据。备注部分甚至提到了这一点:

For nonattached properties, the metadata type returned by this property cannot be cast to derived types of PropertyMetadatatype, 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.

For attached properties, the type of the metadata returned by this property will match the type given in the original RegisterAttachedregistration method.

对于非附加属性,此属性返回的元数据类型无法转换为PropertyMetadata类型的派生类型,即使该属性最初是使用派生元数据类型注册的。如果您希望原始注册的元数据包括其原始可能派生的元数据类型,请改为调用GetMetadata(Type),将原始注册类型作为参数传递。

对于附加属性,此属性返回的元数据类型将与原始RegisterAttached注册方法中给出的类型相匹配。

This is clearly visible in the provided code. Little hints are also hidden in the registering methods, i.e. for RegisterAttachedthe metadata parameter is named defaultMetadata, whereas for Registerit 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 PropertyMetadatawith only DefaultValueset (either from provided metadata or automatically). Only the subsequent call to OverrideMetadataactually uses the provided metadata.

这在提供的代码中清晰可见。注册方法中也隐藏了一些提示,即RegisterAttached元数据参数是 named defaultMetadata,而Register它的参数是 named typeMetadata。对于附加属性,提供的元数据成为默认元数据。但是,在常规属性的情况下,默认元数据始终是PropertyMetadatawith only DefaultValueset的新实例(来自提供的元数据或自动)。只有后续调用OverrideMetadata实际使用提供的元数据。

Consequences

结果

The main practical difference is that in case of regular properties the CoerceValueCallbackand PropertyChangedCallbackare applicable onlyfor types derived from the type declared as the owner type, and for attached properties they're applicable for alltypes. E.g. in this scenario:

主要的实际区别是,在常规性的情况下,CoerceValueCallbackPropertyChangedCallback适用只有从声明为所有者类型类型派生类型,以及附加属性它们适用于所有类型。例如在这种情况下:

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

the registered PropertyChangedCallbackwill be calledif the property was registered as an attached property, but will not be calledif it was registered as a regular property. Same goes to CoerceValueCallback.

如果该属性被注册为附加属性,则注册PropertyChangedCallback将被调用,但如果它被注册为常规属性,则不会被调用。同样去CoerceValueCallback

A secondary difference stems from the fact that OverrideMetadatarequires that supplied type derives from DependencyObject. In practice it means that the owner type for regular properties must derivefrom DependencyObject, whereas for attached properties in can be anytype (including static classes, structs, enums, delegates, etc.).

次要差异源于这样一个事实,即OverrideMetadata要求提供的类型来自DependencyObject. 实际上,这意味着常规属性的所有者类型必须派生DependencyObject,而对于附加属性 in 可以是任何类型(包括静态类、结构、枚举、委托等)。

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 ifthe class that this element represents has a CLRproperty of that name
  • Explicit name syntax can be used on an element if and only ifthe class specified by the first part of the full name exposes appropriate static get/setmethods (referred to as accessors) with names matching the second part of the full name
  • 当且仅当此元素表示的类具有该名称的CLR属性时,才能在元素上使用隐式名称语法
  • 当且仅当全名第一部分指定的类公开了名称与全名第二部分匹配的适当静态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 Studiocode snippets) instruct you to use CLRproperty 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.

现在提到的误解是由于绝大多数教程(连同库存Visual Studio代码片段)指示您对常规依赖属性使用CLR属性,并为附加属性使用get/set 访问器这一事实造成的。但是没有什么可以阻止您同时使用两者,允许您使用您喜欢的任何语法。

回答by shweta

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.

附加属性基本上用于容器元素。就像如果你有一个网格并且你有 grid.row 现在这被认为是网格元素的附加属性。你也可以在 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.

依赖属性就像属性基本上属于某个其他类并在其他类中使用。例如:就像你有一个矩形,这里的 height 和 width 是矩形的常规属性,但 left 和 top 是依赖属性,因为它属于 Canvass 类。

回答by spspli

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.

我认为您可以在类本身中定义附加属性,也可以在另一个类中定义它。我们总是可以使用附加属性来扩展标准的微软控件。但是依赖属性,你在你自己的自定义控件中定义它。例如,您可以从标准控件继承您的控件,并在您自己的控件中定义一个依赖属性并使用它。这相当于定义了一个附加属性,并在标准控件中使用这个附加属性。

回答by Mukesh

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.

附加属性是一种特殊的 DependencyProperties。它们允许您将一个值附加到一个对该值一无所知的对象上。这个概念的一个很好的例子是布局面板。每个布局面板需要不同的数据来对齐其子元素。Canvas 需要 Top 和 Left,DockPanel 需要 Dock,等等。既然你可以写自己的布局面板,列表是无限的。所以你看,不可能在所有 WPF 控件上都拥有所有这些属性。解决方案是附加属性。它们由在特定上下文中需要来自另一个控件的数据的控件定义。例如,由父布局面板对齐的元素。