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;
}
}
}