- A+
理解依赖项属性
依赖项属性是专门为WPF创建的,在WPF的核心特征中使用。
创建依赖项属性
public class DP: DependencyObject { //声明依赖项属性 public static readonly DependencyProperty MydpProperty; static DP() { //指示依赖属性使用什么服务(如数据绑定、动画以及日志) FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsMeasure); //注册依赖属性 MydpProperty = DependencyProperty.Register("Mydp", typeof(double), typeof(DP), metadata, new ValidateValueCallback(ShirtValidateCallback)); //以上Register方法参数 //1、属性名 2、属性数据类型 3、拥有该属性的类型 4、附加属性设置的FrameworkPropertyMetadata对象(可选) 5、验证属性的回调函数(可选) } //添加属性包装器 DependencyObject.SetValue() or DependencyObject.GetValue() //DependencyObject.Clear(DP.MydpProperty) 删除本地值设置 public double Mydp { set { SetValue(MydpProperty, value); } get { return (double)GetValue(MydpProperty); } } private static bool ShirtValidateCallback(object value) { return true; } }
使用依赖项属性
依赖项属性的两个关键行为——更改通知和动态值识别。Visual Studio快捷键 (propdp+Tab)
-
更改通知:当属性值发生改变,依赖项属性不会自动引发事件,以通知一个属性值发生变化。而是触发一个受保护的OnPropertyChangedCallBack()方法,该方法通过两个WPF服务(数据绑定和触发器)传递信息,并调用PropertyChangedCallback回调函数。
-
动态值识别:按照一定优先级来检索基本值,检索改变属性值的提供者。
优先级由小到大让如下:
1、默认值(FrameworkPropertyMetadata对象设置的值)。2、继承而来的值。3、主题样式值。 4、项目样式值。5、本地值(元素对象直接设置的值)
1、基本值(如上)。2、表达式值(数据绑定和资源)3、动画的目标,应用该动画。4、运行CoerceValueback回调函数修的正属性值。
共享的依赖项属性
DependencyProperty.AddOwner(Typeof(DependencyObject)); //注册在TextElement静态构造函数中,在TextBlock静态构造函数中只是简单的重用 TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));
附加属性
注册附加属性使用RegisterAttached()方法,与注册依赖属性相同,附加属性不设置属性包装器,通过调用两个静态方法来设置和获取属性值。Visual Studio快捷键
(propa+Tab)
//注册附加属性 public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached("Password", typeof(string), typeof(ADP), new PropertyMetadata(string.Empty)); public static string GetPassword(DependencyObject obj) { return (string)obj.GetValue(PasswordProperty); } public static void SetPassword(DependencyObject obj, string value) { obj.SetValue(PasswordProperty, value); }
//这段代码不会抛异常,这是因为Button不会去找他不知道的属性值 Button btn = sender as Button; btn.SetValue(PasswordBox.PasswordCharProperty, '*'); //PasswordCharProperty是属于PasswordBox的属性值,所以密码图形会发生改变。 if (this.pwd is PasswordBox passwordbox) { passwordbox.SetValue(PasswordBox.PasswordCharProperty, '*'); }
示例,以PasswordBox添加附加属性来对密码进行binding。
public class PasswordHelper { /// <summary> /// 回调函数中避免无意义的赋值操作 /// </summary> static bool _isUpdate = false; /// <summary> /// PasswordProperty 附加属性的功能就像是一个桥梁,通过回调函数和密码框事件来实现数据通知。 /// </summary> public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached("Password", typeof(string), typeof(PasswordHelper), new PropertyMetadata(string.Empty, new PropertyChangedCallback(PasswordChangedCallBack))); public static string GetPassword(DependencyObject obj) { return (string)obj.GetValue(PasswordProperty); } public static void SetPassword(DependencyObject obj, string value) { obj.SetValue(PasswordProperty, value); } private static void PasswordChangedCallBack(DependencyObject o, DependencyPropertyChangedEventArgs e) { //当附加属性PasswordHelper.PasswordProperty发生改变时,触发此回调方法 PasswordBox passwordbox = o as PasswordBox; passwordbox.PasswordChanged -= Passwordbox_PasswordChanged; if (!_isUpdate) { //PasswordProperty绑定属性发生变化时,将PasswordProperty值赋给界面PasswordBox的Password属性 passwordbox.Password = e.NewValue?.ToString(); } passwordbox.PasswordChanged += Passwordbox_PasswordChanged; } private static void Passwordbox_PasswordChanged(object sender, RoutedEventArgs e) { //当界面PasswordBox的Password发生变化时触发此事件处理器。 PasswordBox passwordBox = sender as PasswordBox; _isUpdate = true; //当密码值发生变化时,将密码赋值给附加属性PasswordProperty SetPassword(passwordBox, passwordBox.Password); _isUpdate = false; } public static bool GetAttach(DependencyObject obj) { return (bool)obj.GetValue(AttachProperty); } public static void SetAttach(DependencyObject obj, bool value) { obj.SetValue(AttachProperty, value); } //初始化时为PasswordBox的PasswordChanged事件添加事件处理器 public static readonly DependencyProperty AttachProperty = DependencyProperty.RegisterAttached("Attach", typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, new PropertyChangedCallback(AttachChangedCallBack))); private static void AttachChangedCallBack(DependencyObject o, DependencyPropertyChangedEventArgs e) { PasswordBox passwordBox = o as PasswordBox; if (passwordBox != null) return; //当旧值是true时,清理事件处理器 if ((bool)e.OldValue) { passwordBox.PasswordChanged -= Passwordbox_PasswordChanged; } //当此附加属性设置为True时添加事件处理启 if ((bool)e.NewValue) { passwordBox.PasswordChanged += Passwordbox_PasswordChanged; } } }
<PasswordBox Width="200" Height="20" Margin="0,10" local:PasswordHelper.Attach="True" local:PasswordHelper.Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
属性验证
WPF提供两种方法来阻止非法值:
- ValidateValueCallback:该回调函数可接受或拒绝新值,通常用与捕获违反属性约束的明显错误。作为DependencyProperty.Register()的一个参数提供该回调函数。
- CoerceValueCallback:该回调函数能将新值修改为更能接受的值。作为FrameworkPropertyMetadata对象的构造函数的一个参数提供该回调函数。
进行属性验证的过程:
1)首先,CoerceValueCallback方法有机会修改提供的值,或者返回DependencyProperty.UnsetValue,这会完全拒绝修改。
2)接下来激活ValiadataValueCallback方法,返回true接受一个合法值,返回false拒绝值。ValiadataValueCallback方法不能访问设置属性的对象,不能检查其他属性值。
3)最后,如果前两个阶段都成功,就会触发PropertyChangedCallback方法。
public class MyBtn : Button { public double Max { get { return (double)GetValue(MaxProperty); } set { SetValue(MaxProperty, value); } } // Using a DependencyProperty as the backing store for Max. This enables animation, styling, binding, etc... public static readonly DependencyProperty MaxProperty = DependencyProperty.Register("Max", typeof(double), typeof(MyBtn), new PropertyMetadata(default(double), new PropertyChangedCallback(MaxPropertyChangedCallback), new CoerceValueCallback(MaxCoerceValueCallback)), new ValidateValueCallback(MaxValidateValueCallback)); //当此回调返回DependencyProperty.UnsetValue时,不会进入PropertyChangedCallback回调函数,回调修改后返回的值类型要与DependncyProperty类型一致,否则会在INorifyPropertyChanged接口的PropertyChanged事件的调用的地方抛出异常。 public static object MaxCoerceValueCallback(DependencyObject sender, object obj) { if (double.Parse(obj.ToString()) > 50) { return (object)50.0; } return obj; #if test return DependencyProperty.UnsetValue; #endif } public static void MaxPropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e) { MyBtn button = (MyBtn)o; button.Height = (double)e.NewValue; } //当此回调函数返回false时,不会再进入CoerceValueCallback和PropertyChangedCallback回调函数,会在INorifyPropertyChanged接口的PropertyChanged事件的调用的地方抛出异常。 public static bool MaxValidateValueCallback(object value) { if (double.TryParse(value.ToString(), out double i)) { return true; } else { return false; } } }