依赖项属性概述

Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展common language runtime (CLR) 属性的功能。 这些服务通常统称为 WPF 属性系统。 由 WPF 属性系统支持的属性称为依赖项属性。本概述介绍 WPF 属性系统以及依赖项属性的功能。这包括如何在 XAML 和在代码中使用现有依赖项属性。 本概述还介绍了依赖项属性所特有的方面(如依赖项属性元数据),并说明了如何在自定义类中创建自己的依赖项属性。

本主题包括下列各节。

  • 先决条件
  • 依赖项属性和 CLR 属性
  • 依赖项属性支持 CLR 属性
  • 设置属性值
  • 由依赖项属性提供的属性功能
  • 依赖项属性值优先级
  • 了解有关依赖项属性的更多信息
  • 相关主题

先决条件

本主题假设您在 CLR 和面向对象的编程方面有一些基础知识。 若要采用本主题中的示例,还应当了解 XAML 并知道如何编写 WPF 应用程序。 有关更多信息,请参见 演练:开始使用 WPF

依赖项属性和 CLR 属性

在 WPF 中,属性通常公开为common language runtime (CLR) 属性。 在基本级别,您可以直接与这些属性交互,而不必了解它们是以依赖项属性的形式实现的。 但是,您应当熟悉 WPF 属性系统的部分或全部功能,才能利用这些功能。

依赖项属性的用途在于提供一种方法来基于其他输入的值计算属性值。 这些其他输入可以包括系统属性(如主题和用户首选项)、实时属性确定机制(如数据绑定和动画/演示图板)、重用模板(如资源和样式)或者通过与元素树中其他元素的父子关系来公开的值。 另外,可以通过实现依赖项属性来提供独立验证、默认值、监视其他属性的更改的回调以及可以基于可能的运行时信息来强制指定属性值的系统。 派生类还可以通过重写依赖项属性元数据(而不是重写现有属性的实际实现或者创建新属性)来更改现有属性的某些具体特征。

在 SDK 参考中,可以根据某个属性的托管引用页上是否存在“依赖项属性信息”部分来确定该属性是否为依赖项属性。 “依赖项属性信息”部分包括一个指向该依赖项属性的 DependencyProperty 标识符字段的链接,还包括一个为该属性设置的元数据选项的列表、每个类的重写信息以及其他详细信息。

依赖项属性支持 CLR 属性

依赖项属性和 WPF 属性系统通过提供一个支持属性的类型来扩展属性功能,这是使用私有字段支持该属性的标准模式的替代实现方法。 该类型的名称是 DependencyProperty。 定义 WPF 属性系统的另一个重要类型是 DependencyObjectDependencyObject 定义可以注册和拥有依赖项属性的基类。

下面汇集了在本software development kit (SDK) 文档中,在讨论依赖项属性时所使用的术语:

  • **依赖项属性:**一个由 DependencyProperty 支持的属性。

  • **依赖项属性标识符:**一个 DependencyProperty 实例,在注册依赖项属性时作为返回值获得,之后将存储为一个类静态成员。 对于与 WPF 属性系统交互的许多 APIs,此标识符用作一个参数。

  • **CLR“包装”:**属性的实际 get 和 set 实现。 这些实现通过在 GetValueSetValue 调用中使用依赖项属性标识符来合并此标识符,从而使用 WPF 属性系统为属性提供支持。

下面的示例定义 IsSpinning 依赖项属性,并说明 DependencyProperty 标识符与它所支持的属性之间的关系。

Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning",
                                GetType(Boolean),
                                GetType(MyCode))

Public Property IsSpinning() As Boolean
    Get
        Return CBool(GetValue(IsSpinningProperty))
    End Get
    Set(ByVal value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property
public static readonly DependencyProperty IsSpinningProperty = 
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),


...


    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

属性以及支持它的 DependencyProperty 字段的命名约定非常重要。 字段总是与属性同名,但其后面追加了 Property 后缀。 有关此约定及其原因的更多信息,请参见自定义依赖项属性

设置属性值

可以在代码或 XAML 中设置属性。

在 XAML 中设置属性值

下面的 XAML 示例将按钮的背景色指定为红色。 此示例演示这样一种情况:在所生成的代码中,WPF XAML 分析器将 XAML 特性的简单字符串值类型转换为 WPF 类型(一种 Color,通过 SolidColorBrush)。

<Button Background="Red" Content="Button!"/>

XAML 支持多种设置属性的语法格式。 要对特定的属性使用哪种语法取决于该属性所使用的值类型以及其他因素(例如,是否存在类型转换器)。 有关属性设置的 XAML 语法的更多信息,请参见 XAML 概述 (WPF)XAML 语法详述

作为非特性语法的示例,下面的 XAML 示例显示了另一种按钮背景。 这一次不是设置简单的纯色,而是将背景设置为图像,用一个元素表示该图像并将该图像的源指定为嵌套元素的特性。 这是属性元素语法的示例。

<Button Content="Button!">
  <Button.Background>
    <ImageBrush ImageSource="wavy.jpg"/>
  </Button.Background>
</Button>

在代码中设置属性

在代码中设置依赖项属性值通常只是调用由 CLR“包装”公开的 set 实现。 

        Dim myButton As New Button()
        myButton.Width = 200.0
Button myButton = new Button();
myButton.Width = 200.0;

获取属性值实质上也是在调用 get“包装”实现:

        Dim whatWidth As Double
        whatWidth = myButton.Width
double whatWidth;
whatWidth = myButton.Width;

您还可以直接调用属性系统 APIs GetValueSetValue。 如果您使用的是现有属性,则上述操作通常不是必需的(使用包装会更方便,并能够更好地向开发人员工具公开属性)。但是在某些情况下适合直接调用 APIs。

还可以在 XAML 中设置属性,然后通过代码隐藏在代码中访问这些属性。 有关详细信息,请参见WPF 中的代码隐藏和 XAML

由依赖项属性提供的属性功能

依赖项属性提供用来扩展属性功能的功能,这与字段支持的属性相反。 每个这样的功能通常都表示或支持整套 WPF 功能中的特定功能:

  • 资源

  • 数据绑定

  • 样式

  • 动画

  • 元数据重写

  • 属性值继承

  • WPF 设计器集成

资源

依赖项属性值可以通过引用资源来设置。 资源通常指定为页面根元素或应用程序的 Resources 属性值(通过这些位置可以非常方便地访问资源)。 下面的示例演示如何定义 SolidColorBrush 资源。

<DockPanel.Resources>
  <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</DockPanel.Resources>

在定义了某个资源之后,可以引用该资源并使用它来提供属性值:

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

这个特定资源称为 DynamicResource 标记扩展(在 WPF XAML 中,可以使用静态或动态资源引用)。 若要使用动态资源引用,必须设置为依赖项属性,因此它是由 WPF 属性系统明确启用的动态资源引用用法。 有关更多信息,请参见资源概述

注意注意

资源被视为本地值,这意味着,如果您设置另一个本地值,该资源引用将被消除。有关更多信息,请参见依赖项属性值优先级

数据绑定

依赖项属性可以通过数据绑定来引用值。 数据绑定通过特定标记扩展语法(在 XAML 中)或 Binding 对象(在代码中)起作用。 使用数据绑定,最终属性值的确定将延迟到运行时,在运行时,将从数据源获取属性值。

下面的示例使用在 XAML 中声明的绑定来设置 ButtonContent 属性。 该绑定使用一个继承的数据上下文和一个 XmlDataProvider 数据源(未显示出来)。 绑定本身通过数据源中的 XPath 指定所需的源属性。

<Button Content="{Binding XPath=Team/@TeamName}"/>
注意注意

绑定被视为本地值,这意味着,如果您设置另一个本地值,该绑定将被消除。有关详细信息,请参见依赖项属性值优先级

依赖项属性或 DependencyObject 类本身并不支持 INotifyPropertyChanged,以便为数据绑定操作生成有关 DependencyObject 源属性值变化的通知。 有关如何创建要用在数据绑定中并且可以向数据绑定目标报告变化的属性的更多信息,请参见数据绑定概述

样式

样式和模板是使用依赖项属性的两个主要激发方案。 在设置定义应用程序user interface (UI) 的属性时,样式尤其有用。 在 XAML 中,通常将样式定义为资源。 样式与属性系统交互,因为它们通常包含特定属性的“setter”,以及基于另一个属性的实时值更改属性值的“trigger”。

下面的示例创建一个非常简单的样式(该样式将在 Resources 字典中定义,未显示出来),然后将该样式直接应用于 ButtonStyle 属性。 样式中的 setter 将带样式的 ButtonBackground 属性设置为 green。

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>

有关更多信息,请参见样式设置和模板化

动画

可以对依赖项属性进行动画处理。 在应用和运行动画时,经过动画处理的值的操作优先级将高于该属性以其他方式具有的任何值(如本地值)。

下面的示例对 Button 属性的 Background 进行动画处理(在技术上,Background 是通过使用属性元素语法将空白 SolidColorBrush 指定为 Background 来进行动画处理的,之后,该 SolidColorBrushColor 属性就是直接进行动画处理的属性)。

<Button>I am animated
  <Button.Background>
    <SolidColorBrush x:Name="AnimBrush"/>
  </Button.Background>
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation
            Storyboard.TargetName="AnimBrush" 
            Storyboard.TargetProperty="(SolidColorBrush.Color)"
            From="Red" To="Green" Duration="0:0:5" 
            AutoReverse="True" RepeatBehavior="Forever" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

有关对属性进行动画处理的更多信息,请参见动画概述演示图板概述

元数据重写

在从最初注册依赖项属性的类派生时,可以通过重写依赖项属性的元数据来更改该属性的某些行为。 对元数据的重写依赖于 DependencyProperty 标识符。 重写元数据不需要重新实现属性。 元数据的变化是由属性系统在本机处理的;对于所有从基类继承的属性,每个类都有可能基于每个类型保留元数据。

下面的示例重写依赖项属性 DefaultStyleKey 的元数据。 重写这个特定的依赖项属性的元数据是某个实现模式的一部分,该模式创建可以使用主题中的默认样式的控件。

  Public Class SpinnerControl
      Inherits ItemsControl
      Shared Sub New()
          DefaultStyleKeyProperty.OverrideMetadata(GetType(SpinnerControl), New FrameworkPropertyMetadata(GetType(SpinnerControl)))
      End Sub
  End Class
public class SpinnerControl : ItemsControl
{
    static SpinnerControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl), 
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
    }
}

有关重写或获取属性元数据的更多信息,请参见依赖项属性元数据

属性值继承

元素可以从其在对象树中的父级继承依赖项属性的值。

注意注意

属性值继承行为并未针对所有的依赖项属性在全局启用,因为继承的计算时间确实会对性能产生一定的影响。属性值继承通常只有在特定方案指出适合使用属性值继承时才对属性启用。可以通过在 SDK 参考中查看某个依赖项属性的“依赖项属性信息”部分,来确定该依赖项属性是否继承属性值。

下面的示例演示一个绑定,并设置指定绑定(在前面的绑定示例中未显示出来)的源的 DataContext 属性。 子对象中的任何后续绑定都不需要指定资源,而是可以使用从父 StackPanel 对象中的 DataContext 继承的值。 (此外,子对象可以选择直接定义其自身的 DataContextBinding 中的 Source,也可以选择对其绑定的数据上下文故意不使用继承值。)

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}">
  <Button Content="{Binding XPath=Team/@TeamName}"/>
</StackPanel>

有关更多信息,请参见属性值继承

WPF 设计器集成

如果自定义控件具有实现为依赖项属性的属性,则它将收到相应的 适用于 Visual Studio 的 WPF 设计器支持。 一个示例就是能够在**“属性”**窗口中编辑直接依赖项属性和附加依赖项属性。 有关更多信息,请参见控件创作概述

依赖项属性值优先级

当您获取依赖项属性的值时,可能会获得通过其他参与 WPF 属性系统且基于属性的任一输入而在该属性上设置的值。 由于存在依赖项属性值优先级,使得属性获取值的方式的各种方案得以按可预测的方式交互。

请看下面的示例。 该示例包括一个应用于所有按钮及其 Background 属性的样式,但是之后还指定了一个具有在本地设置的 Background 值的按钮。

注意注意

SDK 文档在讨论依赖项属性时有时会使用“本地值”或“本地设置的值”等术语。本地设置的值是指在代码中直接为对象实例设置的属性 (Property) 值,或者在 XAML 中设置为元素属性 (Attribute) 的属性 (Property) 值。

原则上,对于第一个按钮,该属性会设置两次,但是仅应用了一个值,即具有最高优先级的值。 本地设置的值具有最高优先级(对于正在运行的动画除外,但是在本示例中没有应用动画),因此,对于第一个按钮的背景将使用本地设置的值,而不使用样式 setter 值。 第二个按钮没有本地值(而且没有其他比样式 setter 优先级更高的值),因此该按钮中的背景将来自样式 setter。

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
     <Setter Property="Background" Value="Red"/>
    </Style>
  </StackPanel.Resources>
  <Button Background="Green">I am NOT red!</Button>
  <Button>I am styled red</Button>
</StackPanel>

为什么存在依赖项属性优先级?

通常,您不会希望总是应用样式,而且不希望样式遮盖单个元素的哪怕一个本地设置值(否则,通常将很难使用样式或元素)。 因此,来自样式的值的操作优先级将低于本地设置的值。 有关依赖项属性以及它的有效值可能来自何处的更完整列表,请参见依赖项属性值优先级

注意注意

在 WPF 元素定义了许多非依赖项属性的属性。一般说来,只有在需要支持至少一个由属性系统启用的方案(数据绑定、样式、动画、默认值支持、继承、附加属性或失效)时,才将属性实现为依赖项属性。

了解有关依赖项属性的更多信息

  • 附加属性是一种支持 XAML 中的专用语法的属性。 附加属性通常与common language runtime (CLR) 属性不具有 1:1 对应关系,而且不一定是依赖项属性。 附加属性的典型用途是使子元素可以向其父元素报告属性值,即使父元素和子元素的类成员列表中均没有该属性也是如此。 一个主要方案是,使子元素可以将其在 UI 中的表示方式通知给父级;有关示例,请参见 DockLeft。 有关详细信息,请参见附加属性概述

  • 组件开发人员或应用程序开发人员可能希望创建自己的依赖项属性,以便实现数据绑定或样式支持之类的功能,或者实现对失效和强制指定值的支持。 有关详细信息,请参见自定义依赖项属性

  • 通常,依赖项属性应当被视为公共属性,这些公共属性可以由任何具有实例访问权限的调用方访问,或至少可被这样的调用方发现。 有关更多信息,请参见依赖项属性的安全性

请参见

概念

自定义依赖项属性

只读依赖项属性

XAML 概述 (WPF)

WPF 体系结构