WPF依赖属性

  • WPF依赖属性已关闭评论
  • 207 次浏览
  • A+
所属分类:.NET技术
摘要

理解依赖项属性依赖项属性是专门为WPF创建的,在WPF的核心特征中使用。创建依赖项属性

理解依赖项属性

依赖项属性是专门为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;         }     } }