参考链接
《WPF编程宝典》4.1.1 & 4.1.2 & 4.1.3
《深入浅出WPF》7.2.2
什么是依赖属性
可以通过启用原本是公共语言运行时 (CLR) 属性的属性来支持样式设置、数据绑定、继承、动画和默认值,方法是将该属性作为依赖属性进行实现。 依赖属性是通过调用 Register 方法(或 RegisterReadOnly)在 WPF 属性系统中注册,并且受 DependencyProperty 标识符字段支持的属性。 只有 DependencyObject 类型可以使用依赖属性,但是 DependencyObject 在 WPF 类层次结构中级别很高,因此 WPF 中大部分可用的类都支持依赖属性。 若要详细了解依赖属性和此 SDK 中对依赖属性进行描述所使用的一些术语和约定,请参阅依赖属性概述。
应用场景
在类上实现属性时,只要类派生自 DependencyObject,就可以选择使用 DependencyProperty 标识符支持属性,从而使其成为依赖属性。 不必总是将属性实现为依赖属性,这不一定合适,具体取决于方案需要。 有时,使用私有字段支持属性的通常方法已足够满足需求。 但是,如果要使属性支持以下一个或多个 WPF 功能,则应该将属性实现为依赖属性:
- 需要可以在样式中设置属性。 有关详细信息,请参阅样式设置和模板化。
- 需要属性支持数据绑定。 有关数据绑定依赖属性的详细信息,请参阅绑定两个控件的属性。
- 需要可以使用动态资源引用设置属性。 有关详细信息,请参阅 XAML 资源。
- 需要从元素树中的父元素自动继承属性值。 这种情况下,即使为 CLR 访问也创建了属性包装器,也应该使用 RegisterAttached 方法注册。 有关详细信息,请参阅属性值继承。
- 需要属性可以进行动画处理。 有关详细信息,请参阅 动画概述。
- 需要属性系统在先前的值因属性系统、环境或用户执行的操作而发生更改,或者因读取和使用样式而发生更改时进行报告。 通过使用属性元素据,属性可以指定回调方法,每次属性系统确定属性值已明确改动时将调用此回调方法。 与此相关的一个概念是属性值强制转换。 有关详细信息,请参阅依赖属性回调和验证。
- 需要使用同时也被 WPF 进程使用的已建立的元数据约定,例如报告更改属性值是否需要布局系统重新安排元素的视觉对象。 或者需要能够使用元素据替代,以便派生类可以更改基于元数据的特性,例如默认值。
- 需要自定义控件的属性接收“属性”窗口编辑等 Visual Studio WPF 设计器支持。 有关详细信息,请参阅控件创作概述。
检查这些方案时,还应考虑是否可以通过替代现有依赖属性的元素据而不是通过实现一个全新的属性来实现方案。 元数据替代是否可行取决于方案以及方案与现有 WPF 依赖属性和类中的实现的相似度。 有关替代现有属性上的元素据的详细信息,请参阅依赖属性元素据。
依赖属性的定义
- 依赖属性的数据类型需要声明为DependencyProperty
- 依赖属性声明需要加上public static readonly修饰符
- 依赖属性声明需要遵循一定的命名规则。如果使用 Name 注册一个依赖属性,则为依赖属性定义的标识符字段必须命名为 LocationProperty。如果不遵循此命名模式,则设计器可能无法正确地报告属性,而且属性系统样式应用程序的某些方面可能不会以预期的方式工作。
public static readonly DependencyProperty StateProperty;
依赖属性的注册
Register()
| Register(String, Type, Type))
|
使用指定的属性名称、属性类型和所有者类型注册依赖属性。
| | —- | —- | | Register(String, Type, Type, PropertyMetadata))
|
使用指定的属性名称、属性类型、所有者类型和属性元数据注册依赖属性。
| | Register(String, Type, Type, PropertyMetadata, ValidateValueCallback))
|
使用指定的属性名称、属性类型、所有者类型、属性元数据和属性的值验证回调来注册依赖属性。 |
示例
public static readonly DependencyProperty IsDirtyProperty = DependencyProperty.Register("IsDirty",typeof(Boolean),typeof(AquariumObject3));
public static readonly DependencyProperty PressImgProperty = DependencyProperty.Register("PressImg",typeof(ImageSource),typeof(ImageButton));
验证回调
应用场景
验证输入的值是否合法,如果属性不合法,可以返回false,则属性赋值失败。如果属性合法,返回true,属性值就会更改,并触发PropertyChanged事件
示例
官方示例
// ValidateValueCallback是验证回调// IsValidReading是回调绑定的方法public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register("CurrentReading",typeof(double),typeof(Gauge),new FrameworkPropertyMetadata(Double.NaN,FrameworkPropertyMetadataOptions.AffectsMeasure,new PropertyChangedCallback(OnCurrentReadingChanged),new CoerceValueCallback(CoerceCurrentReading)),new ValidateValueCallback(IsValidReading));public double CurrentReading{get { return (double)GetValue(CurrentReadingProperty); }set { SetValue(CurrentReadingProperty, value); }}private static bool IsValidReading(object value){Gauge temp = (Gauge)value;if(temp.Price < 0)// 从验证回调返回false后,依赖属性会赋值失败return false;else// 从验证回调返回true后,依赖属性会赋值成功,并触发PropertyChanged事件return true;}
个人实践
using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{/// <summary>/// 图片按钮/// </summary>public class ImageButton:Button{/// <summary>/// 依赖属性包装器 对属性值的验证不写在这里,写在验证回调里/// </summary>public ImageSource DefaultImage{get { return (ImageSource)GetValue(DefaultImageProperty); }set { SetValue(DefaultImageProperty, value); }}/// <summary>/// 依赖属性定义/// </summary>public static readonly DependencyProperty DefaultImageProperty =// 依赖属性注册DependencyProperty.Register("DefaultImage", typeof(ImageSource), typeof(ImageButton),// 依赖属性元数据 默认值nullnew PropertyMetadata(null),// 验证回调ValidateValueCallback);/// <summary>/// 验证回调,判断是否有效图片/// </summary>/// <param name="value"></param>/// <returns></returns>private static bool ValidateValueCallback(object value){ImageSource img = value as ImageSource;if (img != null)return true;elsereturn false;}}}
依赖属性元数据
注册依赖属性时,通过属性系统进行注册会创建一个存储属性特征的元素据对象。 如果将属性注册到的简单签名Register,则其中许多特性都具有默认设置。 通过的Register其他签名,可以指定注册属性时所需的元数据。 为依赖属性使用的最常见元数据是为其使用默认值。该默认值适用于使用此属性的新实例。
参考链接
常用元数据类型
三者的区别和应用场景
官方解析
- 如果出于将元数据应用到新依赖属性注册的目的而创建新的元数据实例,则可以选择要使用的元数据类:基 PropertyMetadata 或者某些派生类,例如 FrameworkPropertyMetadata。 通常,应使用 FrameworkPropertyMetadata,尤其是在你的属性与属性系统和 WPF 功能(例如布局和数据绑定)存在任何交互的情况下。 针对更为复杂的方案,还可以从 FrameworkPropertyMetadata 派生以创建自己的元数据报告类,使其成员承载额外的信息。 或者,可以使用 PropertyMetadata 或 UIPropertyMetadata 来传达对实现的功能的支持程度。
- 对于现有属性(AddOwner 或 OverrideMetadata 调用),应始终使用原始注册所用的元数据类型来替代。
- 如果要创建在的FrameworkElement派生类上存在的依赖项属性,则可以使用更专用的元数据类FrameworkPropertyMetadata,而不是基类PropertyMetadata。 类的构造函数FrameworkPropertyMetadata有多个签名,你可以在其中组合指定各种元数据特征。 如果只想指定默认值,请使用采用类型Object为的单个参数的签名。 将该对象参数传递为你的属性的特定于类型的默认值 (提供的默认值必须是你作为调用) 中Register的propertyType参数提供的类型。
- 对于FrameworkPropertyMetadata,你还可以为属性指定元数据选项标志。 元数据选项标志(FrameworkPropertyMetadataOptions 值的组合)。 这些选项指定依赖项对象的特性,如布局或数据绑定,它们与系统进行交互。 注册后这些标记会转换为属性元数据上的不同属性,并用于将某些条件传送给布局引擎等其他进程。
参考链接
个人理解
- FrameworkPropertyMetadata继承自 UIPropertyMetadata ,UIPropertyMetadata 继承自PropertyMetadata,子类在继承父类的属性和方法的同时,又添加了自己独有的属性和方法
- 基础的DefaultValue、CoerceValueCallback、PropertyChangedCallback这三类元数据都具备,而UIPropertyMetadata新增了IsAnimationProhibited属性,可以对动画做启用和禁用。FrameworkPropertyMetadata在UIPropertyMetadata的基础上又新增了很多属性和方法,是三类元数据中功能最强大的。但一个对象的属性越多,它占用的空间就越大,所以如果你只需要用到基础功能,那没必要使用FrameworkPropertyMetadata,选择最小的功能几何即可。
- 如果你需要用到的属性,在FrameworkPropertyMetadata中已经存在,那优先以FrameworkPropertyMetadata创建元数据
- 从三种元数据的非继承属性分析他们的定位
的新增属性:
| CoerceValueCallback | 获取或设置对此元数据中所指定 CoerceValueCallback 实现的引用。 |
|---|---|
| DefaultValue | 获取或设置依赖属性的默认值。 |
| IsSealed | 获取一个值,该值确定是否已通过某种方式将元数据应用于属性,从而导致该元数据实例变为不可变状态。 |
| PropertyChangedCallback | 获取或设置对此元数据中所指定 PropertyChangedCallback 实现的引用。 |
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{class MyButton:Button{public Color UnderlineColor1{get { return (Color)GetValue(UnderlineColor1Property); }set { SetValue(UnderlineColor1Property, value); }}// 场景1:只做名称注册及数据类型限制public static readonly DependencyProperty UnderlineColor1Property =DependencyProperty.Register("UnderlineColor1", typeof(Color), typeof(MyButton));public Color UnderlineColor2{get { return (Color)GetValue(UnderlineColor2Property); }set { SetValue(UnderlineColor2Property, value); }}// 场景2:需要设置默认值public static readonly DependencyProperty UnderlineColor2Property =DependencyProperty.Register("UnderlineColor2", typeof(Color), typeof(MyButton), new PropertyMetadata(Color.FromRgb(255,255,255)));public Color UnderlineColor3{get { return (Color)GetValue(UnderlineColor3Property); }set { SetValue(UnderlineColor3Property, value); }}// 场景3:需要处理PropertyChanged事件或强制回调public static readonly DependencyProperty UnderlineColor3Property =DependencyProperty.Register("UnderlineColor3", typeof(Color), typeof(MyButton),new PropertyMetadata(Color.FromRgb(255, 255, 255), OnUnderlineColorChanged, OnCoerceUnderlineColor));/// <summary>/// 依赖属性的值改变时/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){// 打印下划线颜色Trace.WriteLine($"OldValue:{((Color)e.OldValue).ToString()},NewValue:{((Color)e.NewValue).ToString()}");}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){// 将输入的十六进制颜色字符串转换为Colorstring colorStr = value.ToString();if (colorStr.StartsWith("#") && colorStr.Length == 7){return ColorConverter.ConvertFromString(colorStr);}return Color.FromRgb(255, 255, 255);}}}
的新增属性:
| IsAnimationProhibited | 获取或设置一个值,声明是否应在应用了包含元数据实例的依赖项对象上禁用动画。 |
|---|---|
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{// 场景:启用或禁用动画public class ImageButton:Button{/// <summary>/// 是否显示蒙版/// </summary>public Visibility MaskVisibility{get { return (Visibility)GetValue(MaskVisibilityProperty); }set { SetValue(MaskVisibilityProperty, value); }}public static readonly DependencyProperty MaskVisibilityProperty =DependencyProperty.Register("MaskVisibility", typeof(Visibility), typeof(ImageButton),// 依赖属性元数据 最后一个参数为isAnimationProhibited 获取或设置一个值,声明是否应在应用了包含元数据实例的依赖项对象上禁用动画。// 场景:启用或禁用动画new UIPropertyMetadata(Visibility.Collapsed, OnUnderlineColorChanged, OnCoerceUnderlineColor, true));// 依赖属性的值改变时private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){}// 将value的数据类型强制转换为目标依赖属性的数据类型private static object OnCoerceUnderlineColor(DependencyObject d, object value){return new Object();}}}
的新增属性
| FrameworkPropertyMetadataOptions | 元数据选项标志(FrameworkPropertyMetadataOptions 值的组合)。 这些选项指定依赖项对象的特性,如布局或数据绑定,它们与系统进行交互。 |
|---|---|
| UpdateSourceTrigger | 应用此属性的绑定时使用的 UpdateSourceTrigger,其 UpdateSourceTrigger 设置为 Default。 |
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{class ImgButton:Button{public Color UnderlineColor2{get { return (Color)GetValue(UnderlineColor2Property); }set { SetValue(UnderlineColor2Property, value); }}// 场景1:通过FrameworkPropertyMetadataOptions指定框架级属性行为中与 Windows Presentation Foundation (WPF) 属性系统中的特定依赖属性相关的类型。// 例如:FrameworkPropertyMetadataOptions.AffectsRender 更改此依赖属性的值会影响呈现或布局组合的某一方面(不是测量或排列过程)。public static readonly DependencyProperty UnderlineColor2Property =DependencyProperty.Register("UnderlineColor2", typeof(Color), typeof(MyButton),new FrameworkPropertyMetadata(Color.FromRgb(255, 255, 255), FrameworkPropertyMetadataOptions.AffectsRender));public Color UnderlineColor3{get { return (Color)GetValue(UnderlineColor3Property); }set { SetValue(UnderlineColor3Property, value); }}// 场景2:需要同时使用所有可设置属性:默认值、FrameworkPropertyMetadataOptions、属性值改变、强制回调、禁用动画、设置数据绑定值更改触发器类型public static readonly DependencyProperty UnderlineColor3Property =DependencyProperty.Register("UnderlineColor3", typeof(Color), typeof(MyButton),new FrameworkPropertyMetadata(Color.FromRgb(255, 255, 255), FrameworkPropertyMetadataOptions.AffectsRender,OnUnderlineColorChanged, OnCoerceUnderlineColor, true, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));/// <summary>/// 依赖属性的值改变时/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){// 打印下划线颜色Trace.WriteLine($"OldValue:{((Color)e.OldValue).ToString()},NewValue:{((Color)e.NewValue).ToString()}");}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){// 将输入的十六进制颜色字符串转换为Colorstring colorStr = value.ToString();if (colorStr.StartsWith("#") && colorStr.Length == 7){return ColorConverter.ConvertFromString(colorStr);}return Color.FromRgb(255, 255, 255);}}}
默认值
应用场景
在用户未赋值或赋值不符合要求时,会使用默认值,如果依赖属性没有设置默认值,则会报错
个人实践
public Color UnderlineColor2{get { return (Color)GetValue(UnderlineColor2Property); }set { SetValue(UnderlineColor2Property, value); }}// 场景2:需要设置默认值public static readonly DependencyProperty UnderlineColor2Property =DependencyProperty.Register("UnderlineColor2", typeof(Color), typeof(MyButton),new PropertyMetadata(Color.FromRgb(255,255,255)));
强制回调
应用场景
个人实践
示例1
public static readonly DependencyProperty ValueProperty =DependencyProperty.Register("Value", typeof(decimal), typeof(NumericUpDown),new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),new CoerceValueCallback(CoerceValue)));// 通过强制回调限制最大值private static object CoerceValue(DependencyObject element, object value){decimal newValue = (decimal)value;newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));return newValue;}// 属性值更改事件private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args){}
示例2
public static readonly DependencyProperty UnderlineColor3Property =DependencyProperty.Register("UnderlineColor3", typeof(Color), typeof(MyButton),new PropertyMetadata(Color.FromRgb(255, 255, 255), OnUnderlineColorChanged, OnCoerceUnderlineColor));/// 依赖属性的值改变时private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){// 将输入的十六进制颜色字符串转换为Colorstring colorStr = value.ToString();if (colorStr.StartsWith("#") && colorStr.Length == 7){return ColorConverter.ConvertFromString(colorStr);}return Color.FromRgb(255, 255, 255);}
示例3
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{public class ImageButton:Button{public Visibility MaskVisibility{get { return (Visibility)GetValue(MaskVisibilityProperty); }set { SetValue(MaskVisibilityProperty, value); }}public static readonly DependencyProperty MaskVisibilityProperty =DependencyProperty.Register("MaskVisibility", typeof(Visibility), typeof(ImageButton),new UIPropertyMetadata(Visibility.Collapsed, OnUnderlineColorChanged, OnCoerceUnderlineColor, true));/// 依赖属性的值改变时private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){if (value == null)return Visibility.Collapsed;if (string.IsNullOrEmpty(value.ToString()))return Visibility.Collapsed;// 将输入的int值转换为Visibilityif (value.ToString().Equals("0"))return Visibility.Visible;else if (value.ToString().Equals("1"))return Visibility.Hidden;else if (value.ToString().Equals("2"))return Visibility.Collapsed;elsereturn Visibility.Collapsed;}}}
PropertyChanged
应用场景
- 数据改变后做一些逻辑处理
-
个人实践
public static readonly DependencyProperty UnderlineColor3Property =DependencyProperty.Register("UnderlineColor3", typeof(Color), typeof(MyButton),new PropertyMetadata(Color.FromRgb(255, 255, 255), OnUnderlineColorChanged));/// <summary>/// 依赖属性的值改变时/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){// 打印下划线颜色Trace.WriteLine($"OldValue:{((Color)e.OldValue).ToString()},NewValue:{((Color)e.NewValue).ToString()}");}
强制回调、验证回调、PropertyChanged的执行顺序
参考链接
个人理解
在一般情况下,执行顺序是验证回调、强制回调、属性值改变事件,即ValidateValueCallback 、 CoerceValueCallback、PropertyChangedCallback,当我们对依赖属性进行赋值时,首先会校验数据是否符合要求,如果在这一步返回false,则赋值失败,抛出异常。如果数据符合要求,我们可以在强制回调里,转换数据类型或限制最大最小值,最后执行PropertyChangedCallback,此时依赖数据的值已经更改了。我们可以做一些善后工作,或者打印工作。
为依赖属性添加包装器
依赖属性必须通过SetValue(DependencyProperty, Object))赋值,通过GetValue(DependencyProperty))取值,为了方便,我们将依赖属性封装为类似于CLR属性的样子,只需要通过包装器就可以简单实现赋值和取值。
public class MyStateControl : ButtonBase{// 包装器public Boolean State{get { return (Boolean)GetValue(StateProperty); }set { SetValue(StateProperty, value); }}public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));}
实践
官方示例
<MyStateControl State="true"/>
public class MyStateControl : ButtonBase{public MyStateControl() : base() { }public Boolean State{get { return (Boolean)this.GetValue(StateProperty); }set { this.SetValue(StateProperty, value); }}public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));}
个人实践
示例1
// ValidateValueCallback是验证回调// IsValidReading是回调绑定的方法public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register("CurrentReading",typeof(double),typeof(Gauge),new FrameworkPropertyMetadata(Double.NaN,FrameworkPropertyMetadataOptions.AffectsMeasure,new PropertyChangedCallback(OnCurrentReadingChanged),new CoerceValueCallback(CoerceCurrentReading)),new ValidateValueCallback(IsValidReading));public double CurrentReading{get { return (double)GetValue(CurrentReadingProperty); }set { SetValue(CurrentReadingProperty, value); }}private static bool IsValidReading(object value){Gauge temp = (Gauge)value;if(temp.Price < 0)// 从验证回调返回false后,依赖属性会赋值失败return false;else// 从验证回调返回true后,依赖属性会赋值成功,并触发PropertyChanged事件return true;}
示例2
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{/// <summary>/// 图片按钮/// </summary>public class ImageButton:Button{/// <summary>/// 依赖属性包装器/// 是否显示蒙版/// </summary>public Visibility MaskVisibility{get { return (Visibility)GetValue(MaskVisibilityProperty); }set { SetValue(MaskVisibilityProperty, value); }}/// <summary>/// 依赖属性定义/// </summary>public static readonly DependencyProperty MaskVisibilityProperty =// 依赖属性注册DependencyProperty.Register("MaskVisibility", typeof(Visibility), typeof(ImageButton),// 依赖属性元数据 最后一个参数为isAnimationProhibited 获取或设置一个值,声明是否应在应用了包含元数据实例的依赖项对象上禁用动画。new UIPropertyMetadata(Visibility.Collapsed, OnUnderlineColorChanged, OnCoerceUnderlineColor, true));/// <summary>/// 依赖属性的值改变时/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){// 打印Trace.WriteLine($"OldValue:{((Visibility)e.OldValue).ToString()},NewValue:{((Visibility)e.NewValue).ToString()}");}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){if (value == null)return Visibility.Collapsed;if (string.IsNullOrEmpty(value.ToString()))return Visibility.Collapsed;// 将输入的int值转换为Visibilityif (value.ToString().Equals("0"))return Visibility.Visible;else if (value.ToString().Equals("1"))return Visibility.Hidden;else if (value.ToString().Equals("2"))return Visibility.Collapsed;elsereturn Visibility.Collapsed;}}}
示例3
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{class ImgButton:Button{public Color UnderlineColor2{get { return (Color)GetValue(UnderlineColor2Property); }set { SetValue(UnderlineColor2Property, value); }}// 场景1:通过FrameworkPropertyMetadataOptions指定框架级属性行为中与 Windows Presentation Foundation (WPF) 属性系统中的特定依赖属性相关的类型。// 例如:FrameworkPropertyMetadataOptions.AffectsRender 更改此依赖属性的值会影响呈现或布局组合的某一方面(不是测量或排列过程)。public static readonly DependencyProperty UnderlineColor2Property =DependencyProperty.Register("UnderlineColor2", typeof(Color), typeof(MyButton),new FrameworkPropertyMetadata(Color.FromRgb(255, 255, 255), FrameworkPropertyMetadataOptions.AffectsRender));public Color UnderlineColor3{get { return (Color)GetValue(UnderlineColor3Property); }set { SetValue(UnderlineColor3Property, value); }}// 场景2:需要同时使用所有可设置属性:默认值、FrameworkPropertyMetadataOptions、属性值改变、强制回调、禁用动画、设置数据绑定值更改触发器类型public static readonly DependencyProperty UnderlineColor3Property =DependencyProperty.Register("UnderlineColor3", typeof(Color), typeof(MyButton),new FrameworkPropertyMetadata(Color.FromRgb(255, 255, 255), FrameworkPropertyMetadataOptions.AffectsRender,OnUnderlineColorChanged, OnCoerceUnderlineColor, true, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));/// <summary>/// 依赖属性的值改变时/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){// 打印下划线颜色Trace.WriteLine($"OldValue:{((Color)e.OldValue).ToString()},NewValue:{((Color)e.NewValue).ToString()}");}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){// 将输入的十六进制颜色字符串转换为Colorstring colorStr = value.ToString();if (colorStr.StartsWith("#") && colorStr.Length == 7){return ColorConverter.ConvertFromString(colorStr);}return Color.FromRgb(255, 255, 255);}}}
示例4
using System.Diagnostics;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace wpf_control_lib1.Controls{class MyButton:Button{public Color UnderlineColor1{get { return (Color)GetValue(UnderlineColor1Property); }set { SetValue(UnderlineColor1Property, value); }}// 场景1:只做名称注册及数据类型限制public static readonly DependencyProperty UnderlineColor1Property =DependencyProperty.Register("UnderlineColor1", typeof(Color), typeof(MyButton));public Color UnderlineColor2{get { return (Color)GetValue(UnderlineColor2Property); }set { SetValue(UnderlineColor2Property, value); }}// 场景2:需要设置默认值public static readonly DependencyProperty UnderlineColor2Property =DependencyProperty.Register("UnderlineColor2", typeof(Color), typeof(MyButton), new PropertyMetadata(Color.FromRgb(255,255,255)));public Color UnderlineColor3{get { return (Color)GetValue(UnderlineColor3Property); }set { SetValue(UnderlineColor3Property, value); }}// 场景3:需要处理PropertyChanged事件或强制回调public static readonly DependencyProperty UnderlineColor3Property =DependencyProperty.Register("UnderlineColor3", typeof(Color), typeof(MyButton),new PropertyMetadata(Color.FromRgb(255, 255, 255), OnUnderlineColorChanged, OnCoerceUnderlineColor));/// <summary>/// 依赖属性的值改变时/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUnderlineColorChanged(object sender, DependencyPropertyChangedEventArgs e){// 打印下划线颜色Trace.WriteLine($"OldValue:{((Color)e.OldValue).ToString()},NewValue:{((Color)e.NewValue).ToString()}");}/// <summary>/// 将value的数据类型强制转换为目标依赖属性的数据类型/// </summary>/// <param name="d"></param>/// <param name="value"></param>/// <returns></returns>private static object OnCoerceUnderlineColor(DependencyObject d, object value){// 将输入的十六进制颜色字符串转换为Colorstring colorStr = value.ToString();if (colorStr.StartsWith("#") && colorStr.Length == 7){return ColorConverter.ConvertFromString(colorStr);}return Color.FromRgb(255, 255, 255);}}}
