WPF中的依赖项属性和附加属性之间有什么区别?


Answers:


20

抽象

由于我几乎没有找到有关此问题的文档,因此花了一些时间在源代码周围,但这是一个答案。

将依赖项属性注册为常规属性和附加属性(“哲学”属性除外)(将常规属性用于声明类型及其派生类型,将附加属性用作任意 DependencyObject 实例的扩展名)。“哲学的”,因为正如@MarqueIV在对@ReedCopsey的答案的评论中注意到的那样,常规属性也可以用于任意DependencyObject实例。

而且,我不得不不同意其他回答,即附加属性是“依赖属性的类型”,因为它具有误导性-依赖属性没有任何“类型”。框架不在乎该属性是否已注册为附加属性-甚至无法确定(从某种意义上说,由于该信息无关紧要,因此未记录该信息)。实际上,所有属性都像附加属性一样注册,但是在常规属性的情况下,需要执行一些其他操作以稍微改变其行为。

代码摘录

为了避免您自己遍历源代码的麻烦,这里有一个简化的版本。

注册未指定元数据的属性时,调用

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

产生与调用完全相同的结果

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属性获得的默认元数据。备注部分甚至提到了这一点:

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

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

这在提供的代码中清晰可见。小提示也隐藏在注册方法中,例如,RegisterAttached元数据参数命名为defaultMetadata,而元数据参数命名RegistertypeMetadata。对于附加属性,提供的元数据将成为默认元数据。但是,在使用常规属性的情况下,默认元数据始终是PropertyMetadata带有DefaultValue设置的新实例(通过提供的元数据或自动生成)。只有随后的调用才能OverrideMetadata实际使用提供的元数据。

后果

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

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

注册PropertyChangedCallback 将被调用,如果财产被注册为附加属性,但不会被调用,如果它被注册为普通的属性。相同CoerceValueCallback

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

补充

除了@MarqueIV的建议外,我还多次遇到这样的观点,即常规属性和附加属性在XAML中的使用方式不同。即,常规属性需要隐式名称语法,而不是附加属性所需的显式名称语法。从技术上讲这是不正确的,尽管在实践中通常是这种情况。为了清楚起见:

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

纯XAML中,管理这些语法用法的唯一规则如下:

  • 且仅当此元素表示的类具有该名称的CLR属性时,可以在该元素上使用隐式名称语法
  • 当且仅当全名第一部分指定的类公开了适当的静态get / set方法(称为访问)且名称与全名第二部分匹配时,才可以对元素使用显式名称语法。

满足这些条件使您可以使用相应的语法,而不管支持依赖项属性是注册为常规还是附加的。

现在,提到的误解是由以下事实造成的:绝大多数教程(连同库存的Visual Studio代码段)都指示您将CLR属性用于常规依赖项属性,并为附加的依赖项获取/设置访问器。但是没有什么可以阻止您同时使用两者,从而允许您使用自己喜欢的语法。


71

附加属性是一种依赖项属性。不同之处在于它们的使用方式。

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

例如,这允许容器创建可在任何UIelement上使用的属性。

至于实现上的差异-定义属性时,基本上只需要使用Register与RegisterAttached。


10
但是到底有什么区别呢?从我所看到的,您可以通过代码将一个不可连接的属性附加到另一个属性上(我认为这在XAML中被阻止了。)也许这是区别吗?
Mark A. Donohoe

5

附加属性基本上是用于容器元素的。例如,如果您有一个网格并且具有grid.row现在,这被视为一个网格元素的附加属性。也可以在texbox,button等中使用此属性来设置它放置在网格中。

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


-1

附加属性是一种特殊的DependencyProperties。它们允许您将值附加到对该值一无所知的对象。布局面板就是一个很好的例子。每个布局面板需要不同的数据以对齐其子元素。“画布”需要顶部和左侧,“ DockPanel”需要Dock等。由于您可以编写自己的布局面板,因此列表是无限的。如此看来,不可能在所有WPF控件上都具有所有这些属性。解决方案具有附加属性。它们由需要在特定上下文中来自另一个控件的数据的控件定义。例如,由父布局面板对齐的元素。


-1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.