依赖属性概述

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

先决条件

本文假定对 .NET 类型系统和面向对象的编程有基本的了解。 若要遵循本文中的示例,它有助于了解 XAML 以及如何编写 WPF 应用程序。 有关详细信息,请参阅 教程:使用 .NET 创建新的 WPF 应用

依赖属性和 CLR 属性

WPF 属性通常公开为标准 .NET 属性。 你可能在基本级别与这些属性进行交互,并且永远不知道它们作为依赖属性实现。 但是,熟悉 WPF 属性系统的一些或全部功能将帮助你利用这些功能。

依赖属性的目的是提供一种方法,以便根据其他输入的值计算属性的值,例如:

  • 系统属性,如主题和用户首选项。
  • 即时属性确定机制,如数据绑定和动画和情景剧本。
  • 多用途模板,如资源和样式。
  • 通过父子关系与元素树中的其他元素已知的值。

此外,依赖属性还可以提供:

  • 独立验证。
  • 默认值。
  • 用于监视其他属性变化的回调。
  • 一个可以根据运行时信息强制属性值的系统。

派生类可以通过重写依赖属性的元数据来更改现有属性的某些特征,而不是重写现有属性的实际实现或创建新属性。

在 SDK 引用中,可以通过该属性的托管引用页面上是否存在依赖属性信息部分来标识依赖属性。 “依赖属性信息”部分包含指向该依赖属性的 DependencyProperty 标识符字段的链接。 它还包括该属性的元数据选项列表、每类重写信息和其他详细信息。

依赖属性支持 CLR 属性

依赖属性和 WPF 属性系统通过引入一种支持属性的类型来增强属性功能,这是对以专用字段支持属性的标准模式的替代方案。 这种类型的名称是 DependencyProperty。 定义 WPF 属性系统的另一个重要类型是 DependencyObject,它定义可注册并拥有依赖属性的基类。

下面是一些常用的术语:

  • 依赖属性,它是由DependencyProperty支持的属性。

  • 依赖属性标识符,它是 DependencyProperty 注册依赖属性时作为返回值的实例,然后存储为类的静态成员。 与 WPF 属性系统交互的许多 API 使用依赖属性标识符作为参数。

  • CLR“包装器”,它是getset对属性的实现。 这些实现通过在GetValueSetValue调用中使用依赖项属性标识符来整合该标识符。 这样,WPF 属性系统就为该属性提供支持。

下面的示例定义 IsSpinning 依赖项属性,以显示标识符与它所返回的属性的关系 DependencyProperty

public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register(
    "IsSpinning", typeof(bool),
    typeof(MainWindow)
    );

public bool IsSpinning
{
    get => (bool)GetValue(IsSpinningProperty);
    set => SetValue(IsSpinningProperty, value);
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning", GetType(Boolean), GetType(MainWindow))

Public Property IsSpinning As Boolean
    Get
        Return GetValue(IsSpinningProperty)
    End Get
    Set(value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property

属性及其支持性 DependencyProperty 字段的命名约定非常重要。 字段的名称始终为属性的名称,并附加后缀 Property。 有关此约定及其原因的详细信息,请参阅 自定义依赖项属性

设置属性值

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

在 XAML 中设置属性值

下面的 XAML 示例将按钮的背景色设置为红色。 XAML 属性的字符串值由 WPF XAML 分析器转换为 WPF 类型。 在生成的代码中,WPF 类型是一种Color,通过SolidColorBrush实现。

<Button Content="I am red" Background="Red"/>

XAML 支持多种语法形式来设置属性。 要用于特定属性的语法取决于属性使用的值类型,以及其他因素,例如类型转换器的存在。 有关设置属性的 XAML 语法的详细信息,请参阅 WPF 中的 XAMLXAML 语法详细信息

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

<Button Content="I have an image background">
    <Button.Background>
        <ImageBrush ImageSource="stripes.jpg"/>
    </Button.Background>
</Button>

在代码中设置属性

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

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

获取属性值本质上是对get“包装器”实现的调用:

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

还可以直接调用属性系统 API GetValueSetValue。 直接调用 API 适用于某些方案,但通常不适用于使用现有属性的情况。 通常,封装器更方便,并为开发人员工具提供了属性的更好展示。

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

依赖属性提供的属性功能

与字段支持的属性不同,依赖属性扩展属性的功能。 通常,添加的功能表示或支持以下功能之一:

资源

可以通过引用资源来设置依赖项属性值。 资源通常指定为 Resources 页面根元素或应用程序的属性值,因为这些位置提供对资源的方便访问。 在此示例中,我们定义资源 SolidColorBrush

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

定义资源后,可以引用资源来提供属性的值 Background

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

在 WPF XAML 中,可以使用静态或动态资源引用。 此特定资源被引用为DynamicResource

注释

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

数据绑定

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

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

<Button Content="{Binding Source={StaticResource TestData}, XPath=test[1]/@text}"/>

注释

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

依赖属性或 DependencyObject 类本身不支持对数据绑定操作中源属性值更改的通知。 有关如何创建可用于数据绑定的属性的详细信息,该属性可以报告对数据绑定目标所做的更改,请参阅 数据绑定概述

风格

样式和模板是使用依赖属性的令人信服的原因。 样式对于设置定义应用程序 UI 的属性特别有用。 样式通常定义为 XAML 中的资源。 样式与属性系统相互作用,因为它们通常包含用于特定属性的设置器,以及根据其他属性的运行时值来更改属性值的触发器。

以下示例用于创建一个简单的样式,该样式将在 Resources 字典中定义(未显示)。 然后,该样式直接应用于 Style 属性中 Button 的一个元素。 样式中的资源库将带样式 BackgroundButton 属性设置为绿色。

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

有关详细信息,请参阅 样式设置和模板化

动画

依赖项属性可以动画化。 应用动画运行时,动画值优先于任何其他属性值,包括本地值。

下面的示例对 Background 一个 Button. 的属性进行动画处理。 从技术角度来说,属性元素语法将SolidColorBrush设置为空白Background,而SolidColorBrushColor属性是被赋予动画效果的。

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

有关对属性进行动画处理的详细信息,请参阅 动画概述情节提要概述

元数据重写

从最初注册依赖属性的类派生时,可以通过重写其元数据来更改依赖属性的特定行为。 重写元数据依赖于 DependencyProperty 标识符,不需要重新实现属性。 元数据更改由属性系统本机处理。 每个类可能为每个类型保留从基类继承的所有属性的单个元数据。

以下示例替代依赖属性的 DefaultStyleKey 元数据。 重写此特定依赖属性的元数据是实现一种创建控件模式的一部分,该模式允许使用主题中的默认样式。

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

有关重写或访问依赖属性的元数据的详细信息,请参阅 重写依赖属性的元数据

属性值继承

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

注释

不会为所有依赖属性全局启用属性值继承行为,因为继承的计算时间会影响性能。 属性值继承通常只在显示适用性的情况下启用。 可以通过查看 SDK 引用中该依赖属性的 “依赖属性信息 ”部分来检查依赖属性是否继承。

以下示例显示了一个绑定,其中包含 DataContext 用于指定绑定源的属性。 因此,子对象中的绑定不需要指定源,并且可以使用父对象中的StackPanel继承值DataContext。 或者,子对象可以在Binding中直接指定其自己的DataContextSource,而不使用继承的值。

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource TestData}}">
    <Button Content="{Binding XPath=test[2]/@text}"/>
</StackPanel>

有关详细信息,请参阅 属性值继承

WPF 设计器集成

实现为依赖属性的自定义控件可以很好地与 Visual Studio 中的 WPF 设计器集成。 一个示例是能够在 “属性” 窗口中编辑直接和附加的依赖项属性。 有关详细信息,请参阅 控件创作概述

依赖属性值优先级

WPF 属性系统中任何基于属性的输入都可以设置依赖属性的值。 依赖项属性值优先级 存在,以便属性如何以可预测的方式获取其值的各种方案进行交互。

注释

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

下一个示例包括应用于 Background 任何按钮的属性的样式,但指定一个具有本地设置 Background 属性的按钮。 从技术上看,该按钮的属性 Background 设置两次,尽管只有一个值适用 ,但该值的优先级最高。 本地集值具有最高优先级,但此处不存在的正在运行的动画除外。 因此,第二个按钮使用本地设置的Background属性值,而不是样式 setter 值。 第一个按钮没有本地值,或者没有比样式设置器优先级更高的其他值,因此使用属性的样式设置器值Background

<StackPanel>
    <StackPanel.Resources>
        <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Orange"/>
        </Style>
    </StackPanel.Resources>
    <Button>I am styled orange</Button>
    <Button Background="Pink">I am locally set to pink (not styled orange)</Button>
</StackPanel>

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

本地集值优先于样式设置器值,这些值支持对元素属性的本地控制。 有关详细信息,请参阅 从属属性值优先级

注释

WPF 元素上定义的许多属性不是依赖属性,因为依赖属性通常仅在需要 WPF 属性系统的功能时才实现。 这些功能包括数据绑定、样式设置、动画、默认值支持、继承、附加属性和无效。

详细了解依赖项属性

  • 组件开发人员或应用程序开发人员可能希望创建自己的依赖属性来添加功能,例如数据绑定或样式支持,或者无效和值强制支持。 有关详细信息,请参阅 自定义依赖项属性

  • 请考虑依赖属性为公共属性,可供任何有权访问实例的调用方访问或发现。 有关详细信息,请参阅 依赖项属性安全性

  • 附加属性是支持 XAML 中的专用语法的属性类型。 附加属性通常不具有与公共语言运行时属性的 1:1 对应关系,也不一定是依赖属性。 附加属性的主要用途是允许子元素向父元素报告属性值,即使父元素和子元素不包括该属性作为类成员列表的一部分。 一个主要方案是让子元素通知父元素如何在 UI 中显示它们。 有关示例,请参阅 DockLeft。 有关详细信息,请参阅 附加属性概述

另请参阅