WPF 自定义控件学习二,控件基础知识
官方文档:控件编写概述 - WPF | Microsoft Learn
2.1. 依赖属性(Dependency Properties)
2.2. 路由事件(Routed Events)
2.3. 控件模板(Control Templates)
2.4. 样式和主题(Styles and Themes)
2.5. 内容呈现(Content Presentation)
2、依赖属性:WPF 属性系统支持的属性称为依赖属性。可以支持以下内容
1)设置样式中的属性。
2)将属性绑定到数据源。
3)使用动态资源作为属性值。
4)对属性进行动画处理。
2.1、使用标识符DependencyProperty定义,通过调用 DependencyProperty.Register向属性系统注册属性名称,以指定以下内容:
属性的名称。
属性类型。
拥有该属性的类型。
属性的元数据。元数据包含属性的默认值、 CoerceValueCallback 和 PropertyChangedCallback
属性校验(可选)。
如下图代码所示、以及对应的注释。注册一个为Value的依赖属性。其中默认值:2,限制值为0-100,且只为非负数。(注意后面会用到)

对应依赖属性完整代码
// 注册“Value”依赖属性。 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", // 属性名称 typeof(int), // 属性类型 typeof(MyButton), // 拥有该属性的控件类型 // 属性的元数据 new FrameworkPropertyMetadata( 2, // 默认值 new PropertyChangedCallback(OnValueChanged), // 属性更改回调,并指定对应的函数 new CoerceValueCallback(CoerceValue) // 强制回调,并指定对应的函数 ), new ValidateValueCallback(ValidateValue) // 属性值验证回调 (可选),并指定对应的函数 ); // 获取或设置分配给控件的值。CLR包装器 public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } // 验证回调 private static bool ValidateValue(object value) { int val = (int)value; return !int.IsNegative(val); // 不允许负值 } // 强制回调允许你在属性值被设置前对其进行修正或强制转换,确保值在特定范围内。 private static object CoerceValue(DependencyObject element, object value) { int newValue = (int)value; if (newValue < 0) { newValue = 0; } if (newValue > 100) { newValue = 100; } return newValue; } // 属性变更回调在依赖属性的值发生变化时被调用,可以在这里执行相关的响应逻辑。 private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { MyButton control = (MyButton)obj; int newValue = (int)args.NewValue; int oldValue = (int)args.OldValue; // 在这里处理属性值更改的逻辑 control.OnValueChanged(oldValue, newValue); } protected virtual void OnValueChanged(double oldValue, double newValue) { // 可以触发事件或执行其他操作 }
3、路由事件:可以支持以下内容:
1)、可以在多个控件的父级上处理事件。
2)、路由事件可在一个 EventSetter对象中使用,这使应用程序开发人员能够在样式中指定事件的处理程序。
3)、路由事件可用于 EventTrigger,这对于使用XAML对属性进行动画处理非常有用。
3.1、路由事件的类型:
1)、冒泡路由(Bubbling Routing) : 事件从触发元素开始,沿着元素树向上冒泡,依次触发父元素、祖父元素...直到根元素的事件处理程序。这是最常见的路由类型。
2)、隧道路由(Tunneling Routing) : 事件从元素树的根元素开始,沿着元素树向下隧道,依次触发祖父元素、父元素...直到触发元素的事件处理程序。隧道路由事件通常以 "Preview" 开头,例如 PreviewMouseDown。
3)、直接路由(Direct Routing) : 事件只在触发元素自身上触发,不进行路由传播。CLR 事件通常是直接路由事件。
3.2、使用标识符RoutedEvent定义对应的事件,通过调用 EventManager.RegisterRoutedEvent 方法注册路由事件,以指定以下内容:
事件的名称
路由策略
事件处理程序的类型
事件所属的控件类型
如下图代码所示、以及对应的注释。注册一个为ValueChanged的路由事件。

对应路由事件完整代码
// 注册 ValueChanged 的路由事件 public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent( "MyValueChanged", // 事件名称 RoutingStrategy.Bubble, // 路由策略 (冒泡) typeof(RoutedPropertyChangedEventHandler<int>), // 事件处理程序的类型 typeof(MyButton) // 事件所属的控件类型 ); // 当Value属性发生更改时触发。创建CLR事件包装器(供XAML绑定) public event RoutedPropertyChangedEventHandler<int> MyValueChanged { add { AddHandler(ValueChangedEvent, value); } remove { RemoveHandler(ValueChangedEvent, value); } } // 当值改变、引发ValueChanged事件。 protected virtual void OnValueChanged(int oldValue, int newValue) { // 创建已注册和包装的本身的路由事件实例,并引发它 RoutedPropertyChangedEventArgs<int> args = new RoutedPropertyChangedEventArgs<int>(oldValue, newValue); args.RoutedEvent = ValueChangedEvent; RaiseEvent(args); // 触发自定义路由事件 }
4、示例、学习完依赖属性与路由事件,想给出一个示例以便于大家理解,
1)给MainWindow.xaml中的自定义控件绑定依赖属性与路由事件,如下图所示。

2)写后台代码的逻辑,自定义按键双击事件触发后,修改他的值为102超过100,并输出对应的值

3)运行结果
可以看到,提示框的值为100,可知在触发依赖属性的强制回调时候,会把值限制为0-100

完整项目源码:https://files.cnblogs.com/files/blogs/721038/UpperComputer-%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E4%B8%8E%E8%B7%AF%E7%94%B1%E4%BA%8B%E4%BB%B6%E9%9B%86%E5%90%88.7z?t=1767278960&download=true

浙公网安备 33010602011771号