WPF中的附加属性是如何工作的?

27

我有点困惑附加属性实际上如何将它们的值传递给父元素或子元素。 TextElement.FontFamily 会导致子元素继承分配给该属性的值(看似下游操作,从父级到子级)。 Grid.Column 会导致父项在特定位置显示该子项(看似上游操作,从子级到父级)。 附加属性值是如何知道向上流动还是向下流动的?我的理解是否不正确,还是缺少某些信息可以让这一切变得清晰明了?

<StackPanel TextElement.FontFamily="Wingdings">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Button Grid.Column="1" Content="My Button"/>
    </Grid>
</StackPanel>
3个回答

46
这里有两个概念:依赖属性和附加依赖属性。 "附加属性"是依赖属性,因此支持属性值继承
关于基本的依赖属性,一个非常粗略的说法是它们基本上从 WPF(逻辑/视觉)树中的父元素继承其值。如果依赖属性(附加或非附加)的元数据设置了FrameworkPropertyMetadataOptions.Inherit标志,则其值会向下继承,并且在许多情况下都是如此。
附加属性是可以通过DependencyObject.SetValue方法在任何 WPF 对象(基本上是至少一个 DependencyObject)上设置的属性。这种机制的目的是“附加”到其他对象需要的父对象信息,而不是子对象本身。例如,Grid.Row 是 Grid 所需的附加属性,用于将项目放置在其呈现区域内。
依赖属性由 WPF 对象系统自动向下继承。
附加属性在特定对象的代码中被明确地"向上"检查。在Grid的情况下,在确定放置其项目的位置时,它会检查每个包含项目上的Grid.Row和Grid.Column附加属性的值。
还经常使用创建自定义附加属性的技术,这些属性以某种方式修改它们所附加到的对象(例如,通过附加属性使得拖放功能成为可能)。
另外需要注意的是,一个好的继承附加属性的例子是TextElement.FontFamily。Grid.Row和Grid.Column属性没有设置Inherits标志。
来自Reflector的TextElement.FontFamily:
FontFamilyProperty = DependencyProperty.RegisterAttached("FontFamily", typeof(FontFamily), typeof(TextElement), new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(TextElement.IsValidFontFamily));

Grid.Row,来自 Reflector:

RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged)), new ValidateValueCallback(Grid.IsIntValueNotNegative));

1
有趣且富有信息性。不过,考虑以下情况:一个2x2的网格(A)包含一个2x2的网格(B)。网格B包含一个按钮。如果网格B的Grid.Column附加属性设置为1(因此它出现在其父网格Grid A的第二列中),那么该附加属性是否应向下级传递到网格B的按钮,以便按钮出现在网格B的第二列? - Mark Carpenter

2

来自MSDN

虽然可以在任何对象上设置附加属性,但这并不意味着设置该属性将产生有形的结果,或者该值将被另一个对象使用。通常情况下,附加属性旨在使来自各种可能的类层次结构或逻辑关系的对象向定义附加属性的类型报告通用信息。定义附加属性的类型通常遵循以下模型之一:

  • 定义附加属性的类型设计为可以是将设置附加属性值的元素的父元素。然后,该类型通过内部逻辑迭代其子对象针对某个对象树结构获取值,并以某种方式对这些值进行操作。

  • 定义附加属性的类型将用作各种可能的父元素和内容模型的子元素。

  • 定义附加属性的类型表示服务。其他类型设置附加属性的值。然后,在评估设置属性的元素时,在服务类的内部逻辑中获取附加属性值。

父元素定义附加属性的示例

WPF定义附加属性的最典型场景是当父元素支持子元素集合,并实现了一种行为,其中每个子元素的具体行为都是单独报告的时候。

DockPanel定义了DockPanel.Dock附加属性,并且DockPanel在其呈现逻辑中有类级别的代码(特别是MeasureOverride和ArrangeOverride)。 DockPanel实例始终会检查其直接子元素是否设置了DockPanel.Dock的值。如果是,则这些值成为应用于该特定子元素的呈现逻辑的输入。嵌套的DockPanel实例分别处理其自己的直接子元素集合,但该行为是特定于DockPanel如何处理DockPanel.Dock值的实现的。理论上可能存在影响超出直接父级的元素的附加属性。如果在没有DockPanel父元素可以操作它的元素上设置了DockPanel.Dock附加属性,则不会引发错误或异常。这仅意味着设置了全局属性值,但它没有当前的DockPanel父元素可以使用该信息。


2
简单来说,这是我理解的方式(如果我错了,请纠正)。
一个对象(A)实现了一个属性,该属性将附加到另一个对象(B)上(对象B甚至不知道这个“可附加”属性的存在)。对象B需要继承自DependencyObject。
对象A还实现了一个静态方法,在其他对象中检查其“可附加”属性,A.GetAttachedProperty(B)。
如果B具有从A继承的附加属性,则A.GetAttachedProperty将读取并返回其值。否则,A将尝试读取它,并返回null,因为它不存在。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接