WPF控件开发之自定义控件(2)
属性的元数据。元数据包含该属性的默认值、CoerceValueCallback 和 PropertyChangedCallback。
通过实现该属性的 get 和 set 访问器,定义一个名为 Value 的 CLR“包装”属性,这个名称也就是用来注册该依赖项属性的名称。请注意,get 和 set 访问器只是分别调用 GetValue 和 SetValue。建议依赖项属性的访问器不要包含其他逻辑,这是因为客户端和 WPF 可绕过这两个访问器直接调用 GetValue 和 SetValue。例如,如果属性绑定到数据源,则不会调用该属性的 set 访问器。不要向 get 和 set 访问器添加其他逻辑,而应使用 ValidateValueCallback、CoerceValueCallback 和 PropertyChangedCallback 委托在值更改时进行响应或检查该值。
为 CoerceValueCallback 定义一个名为 CoerceValue 的方法。CoerceValue 确保 Value 大于或等于 MinValue 且小于或等于 MaxValue。
为 PropertyChangedCallback 定义一个名为 OnValueChanged 的方法。OnValueChanged 创建一个 RoutedPropertyChangedEventArgs<(Of <(T>)>) 对象,并准备引发 ValueChanged 路由事件。路由事件在下一节中讨论。
使用 RoutedEvent 就像依赖项属性以附加功能扩展 CLR 属性的用途一样,路由事件扩展了标准 CLR 事件的用途。在创建新的 WPF 控件时,将事件实现为路由事件也是一种好方法,这是因为路由事件支持以下行为:
事件可以在多个控件的父级上进行处理。如果事件是冒泡事件,则元素树中的单个父级可预订该事件。然后,应用程序作者可以使用一个处理程序来响应多个控件的该事件。例如,如果控件属于 ListBox 中的每个项(因为它包含在 DataTemplate 中),则应用程序开发人员可以为该控件的 ListBox 事件定义相应的事件处理程序。每当这些控件中的任何控件发生该事件时,都会调用该事件处理程序。
路由事件可在 EventSetter 中使用,应用程序开发人员通过 EventSetter 可以在样式内指定事件的处理程序。
路由事件可在 EventTrigger 中使用,这对于使用 XAML 对属性进行动画处理很有用。
下面的示例定义了一个路由事件。
这段代码执行下面的操作:
将一个名为 ValueChangedEvent 的 RoutedEvent 标识符定义为 publicstaticreadonly 字段。
通过调用 EventManager..::.RegisterRoutedEvent 方法注册路由事件。该示例在调用 RegisterRoutedEvent 时指定以下信息:
事件的名称为 ValueChanged。
路由策略为 Bubble,这意味着首先调用源(引发事件的对象)上的事件处理程序,然后从最近的父元素上的事件处理程序开始,相继调用源的各个父元素上的事件处理程序。
该事件处理程序的类型为 RoutedPropertyChangedEventHandler<(Of <(T>)>),是用 Decimal 类型构造的。
该事件的所属类型为 NumericUpDown。
声明一个名为 ValueChanged 的公共事件,并包含事件访问器声明。该示例调用 add 访问器声明中的 AddHandler 和 remove 访问器声明中的 RemoveHandler 来使用 WPF 事件服务。
创建一个名为 OnValueChanged 的受保护的虚方法,该方法引发 ValueChanged 事件。
使用绑定 若要将控件 UI 与其逻辑分离,请考虑使用数据绑定。如果是使用 ControlTemplate 定义控件的外观,这一点尤其重要。使用数据绑定时,可能能够避免引用 UI 的特定部分。
下面的示例更新 NumericUpDown 控件的 TextBlock,向它分配一个名称,然后在代码中按名称引用该文本框。
下面的示例使用绑定来达到相同的目的。
针对设计器设计 若要在 Visual Studio Windows Presentation Foundation (WPF) 设计器中获得对自定义 WPF 控件的支持(例如,使用“属性”窗口编辑属性),请遵循以下准则。
依赖项属性 确保实现 CLRget 访问器和 set 访问器,如前面“使用依赖项属性”中所述。设计器可以使用包装来检测某个依赖项属性是否存在,但与 WPF 和控件客户端一样,在获取或设置属性时不需要使用设计器来调用访问器。
附加属性 应按照以下原则在自定义控件上实现附加属性:
具有一个使用 RegisterAttached 方法创建的 publicstaticreadonlyDependencyProperty,其形式为“属性名称Property”。传递到 RegisterAttached 的属性名称必须与属性名称 匹配。
实现一对名为 Set属性名称 和 Get属性名称 的 public static CLR 方法。这两种方法都应接受从 DependencyProperty 派生的类作为其第一个参数。Set属性名称 方法还接受其类型与属性的注册数据类型匹配的参数。Get属性名称 方法应返回相同类型的值。如果缺少 Set属性名称 方法,则该属性标记为只读。
Set 属性名称 和 Get属性名称 必须分别直接路由到目标依赖项对象的 GetValue 和 SetValue 方法。通过调用方法包装或直接调用目标依赖项对象,设计器可以访问附加属性。