代码之家  ›  专栏  ›  技术社区  ›  kenwarner

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

  •  80
  • kenwarner  · 技术社区  · 15 年前

    在WPF中,(自定义)依赖属性和附加属性有什么区别?它们的用途是什么?实现通常有什么不同?

    5 回复  |  直到 6 年前
        1
  •  66
  •   Reed Copsey    15 年前

    附加属性是一种依赖属性类型。区别在于它们的使用方式。

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

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

    至于实现的差异,基本上只是在定义属性时使用register和registerattached的问题。

        2
  •  6
  •   Grx70    6 年前

    摘要

    因为我几乎找不到有关这件事的文件,所以在 source code 但这里有一个答案。

    将依赖属性注册为常规属性和附加属性之间存在差异,而不是“哲学”属性( 常规属性用于声明类型及其派生类型,附加属性用于任意扩展 DependencyObject 实例 “哲学”,因为正如@marqueiv在对@reedcopsey答案的评论中所注意到的那样,规则属性也可以任意使用。 依赖对象 实例。

    此外,我不同意其他的回答,即附加属性是“依赖属性的类型”,因为它具有误导性——没有任何“类型”的依赖属性。框架不关心该属性是否注册为附加属性-甚至不可能确定(从这个意义上说,这个信息是不被记录的,因为它是不相关的)。事实上,所有属性都被注册为附加属性,但是对于常规属性,还需要做一些额外的事情来稍微修改它们的行为。

    代码摘录

    为了省去自己浏览源代码的麻烦,这里有一个简单的版本。

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

    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 财产。这甚至在 Remarks 章节:

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

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

    这在提供的代码中清晰可见。注册方法中也隐藏了一些提示,例如 RegisterAttached 元数据参数已命名 defaultMetadata 而对于 Register 它的名字是 typeMetadata . 对于附加属性,提供的元数据将成为默认元数据。但是,对于常规属性,默认元数据始终是 PropertyMetadata 仅用 DefaultValue 设置(从提供的元数据或自动设置)。只有后续调用 OverrideMetadata 实际使用提供的元数据。

    后果

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

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

    注册的 属性更改回调 将被称为 如果该财产已登记为附属财产,但 不会被调用 如果注册为普通财产。同样去 强制值回调 .

    二次差异源于以下事实: 覆盖元数据 要求提供的类型派生自 依赖对象 . 实际上,它意味着常规属性的所有者类型 必须推导 依赖对象 ,而对于中的附加属性,可以 任何 类型(包括静态类、结构、枚举、委托等)。

    补充

    除了@marqueiv的建议外,我还经常遇到一些意见,即常规和附加属性在使用方式上有所不同 XAML . 也就是说,常规属性需要隐式名称语法,而不是附加属性所需的显式名称语法。这是技术上的 不是真的 尽管在实践中通常是这样。为了清晰:

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

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

    • 不能在元素上使用隐式名称语法 如果且仅当 此元素表示的类具有 CLR 该名称的属性
    • 显式名称语法不能用于元素 如果且仅当 由全名的第一部分指定的类公开适当的静态 get / set 方法(简称 访问器 )名称与全名的第二部分匹配

    满足这些条件使您能够使用相应的语法,而不管backing dependency属性是注册为regular还是attached。

    现在提到的误解是由绝大多数教程(连同股票)引起的 可视演播室 代码段)指示您使用 CLR 用于常规依赖项属性的属性,以及用于附加属性的get/set访问器。但是没有什么能阻止你同时使用这两种语言,允许你使用任何你喜欢的语法。

        3
  •  5
  •   shweta    12 年前

    附加属性基本上是针对容器元素的。例如,如果您有一个网格,并且您有grid.row,现在这被认为是网格元素的附加属性。此外,您可以在texbox、button等中使用此属性来设置其在网格中的位置。

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

        4
  •  0
  •   spspli    7 年前

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

        5
  •  -1
  •   Mukesh    8 年前

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