属性更改事件 (WPF .NET)

Windows Presentation Foundation(WPF)定义了多个事件,用于响应属性值变化的情况。 属性通常是依赖属性。 事件本身可以是路由事件,也可以是标准公共语言运行时(CLR)事件,具体取决于事件是应通过元素树路由还是仅在属性发生更改的对象上发生。 当属性更改仅与属性值发生更改的对象相关时,后一种方案适用。

先决条件

本文假设对依赖项属性有一个基本的了解,并且你已阅读 路由事件概述

标识属性更改事件

并非所有报告属性更改的事件都通过签名或命名模式显式标识为属性更改事件。 SDK 文档将属性与事件相互参照,并指示事件是否直接关联到属性值的更改。

某些事件使用特定于属性更改事件的事件数据类型和委托。 例如,RoutedPropertyChangedDependencyPropertyChanged 事件都有特定的签名。 以下各节将讨论这些事件类型。

RoutedPropertyChanged 事件

RoutedPropertyChanged 事件具有 RoutedPropertyChangedEventArgs<T> 事件数据和 RoutedPropertyChangedEventHandler<T> 委托。 事件数据和委托都有泛型类型参数 T。 定义处理程序时,指定已更改属性的实际类型。 事件数据包含 OldValueNewValue 属性,其运行时类型与更改的属性相同。

名称中的“Routed”部分表示属性更改事件注册为路由事件。 属性更改 路由 事件的优点是,每当子元素属性发生更改时,父元素都会收到通知。 这意味着当控件的任何复合部件的值发生更改时,控件的顶级元素接收属性更改事件。 例如,假设创建一个包含 RangeBase 控件的控件,例如 Slider。 如果滑块部件上的 Value 属性发生更改,则可以在父控件而不是部件上处理该更改。

避免使用属性更改事件处理程序来验证属性值,因为这不是大多数属性更改事件的设计意图。 通常,会提供属性更改事件,以便您能够在代码的其他逻辑区域中响应值的变化。 建议不要在属性更改事件处理程序中再次更改属性值,并可能导致意外的递归,具体取决于处理程序实现。

如果属性是自定义依赖属性,或者使用已定义实例化代码的派生类,则 WPF 属性系统有更好的方法来跟踪属性更改。 这种方式是使用内置 CoerceValueCallbackPropertyChangedCallback 属性系统回调。 有关如何使用 WPF 属性系统进行验证和强制的详细信息,请参阅 依赖属性回调和验证 以及 自定义依赖属性

DependencyPropertyChanged 事件

DependencyPropertyChanged 事件具有 DependencyPropertyChangedEventArgs 事件数据和 DependencyPropertyChangedEventHandler 委托。 这些事件是标准 CLR 事件,而不是路由事件。 DependencyPropertyChangedEventArgs 是一种不寻常的事件数据报告类型,因为它不派生自 EventArgs,并且不是类。

DependencyPropertyChanged 事件的一个示例是 IsMouseCapturedChangedDependencyPropertyChanged 事件比 RoutedPropertyChanged 事件更为常见。

与 RoutedPropertyChanged 事件数据类似,DependencyPropertyChanged 事件数据包含 OldValueNewValue 属性。 由于前面提到的 原因,请避免使用属性更改事件处理程序再次更改属性值。

属性触发器

与属性更改事件密切相关的概念是属性触发器。 属性触发器是在样式或模板中创建的。 通过属性触发器,可以创建基于分配了触发器的属性的值的条件行为。

属性触发器所依据的属性必须是依赖属性。 它可以是(且通常是)只读依赖属性。 如果控件公开的依赖属性的名称以“Is”开头,则表明该属性至少部分设计为属性触发器。 采用此命名规则的属性通常是只读的 Boolean 依赖属性,其属性的主要作用是报告控件状态。 如果控件状态影响实时 UI,则该依赖属性是一个属性触发器候选项。

某些依赖属性具有专门的属性更改事件。 例如,IsMouseCaptured 具有 IsMouseCapturedChanged 属性更改事件。 IsMouseCaptured 属性是只读的,其值由输入系统修改。 输入系统在每次实时更改时都将引发 IsMouseCapturedChanged 事件。

属性触发器限制

与真正的属性更改事件相比,属性触发器存在一些限制。

属性 通过完全匹配逻辑来触发 的工作,你需要指定一个属性名称和一个特定值来激活该触发器。 例如 <Setter Property="IsMouseCaptured" Value="true"> ... </Setter>。 属性触发器语法将大多数属性触发器用法限制为 Boolean 属性或采用专用枚举值的属性。 可能的值范围必须可管理,以便你可以为每个事例定义触发器。 有时,属性触发器仅适用于特殊值,例如当项计数达到零时。 单个触发器不能设置为在属性值偏离特定值(如零)时激活。 与其对所有非零情况使用多个触发器,不如考虑实现一个代码事件处理程序,或设置一个默认行为,使得值为非零时自动从触发器状态切换。

属性触发器语法类似于编程中的“if”语句。 如果触发器条件为 true,将“运行”属性触发器的“主体”。 属性触发器的“body”不是代码,而是标记语言。 该标记仅限于使用一个或多个 Setter 元素来设置应用样式或模板的对象的其他属性。

当属性触发器的“if”条件具有各种各样的可能值时,建议使用触发器外部的 Setter 将相同的属性值设置为默认值。 这样,当触发条件为 true时,触发器内部的设定者将具有优先权,否则,触发器外部的 Setter 将具有优先权。

属性触发器在需要根据同一元素上另一属性的状态更改一个或多个外观属性的情况下非常有用。

若要深入了解属性触发器,请参阅样式设置和模板化

另请参阅