依赖属性
依赖属性回调方法与参数
具有依赖属性的类必须继承自DependencyObject
,定义依赖属性要有2个步骤
//1属性包装器,目的是为了向正常属性一样使用依赖属性
public int Name
{
get { return (int)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
//2注册依赖属性
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
注册依赖属性第一个参数表示对应包装器的名称,第二个为属性类型,第3个为属于哪个类。
本文着重讲解第四个之后的参数
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback);
- 第四个参数 PropertyMetadata
public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback);
参数 | 备注 |
---|---|
defaultValue | 默认参数 |
propertyChangedCallback | 属性变化回调函数 |
coerceValueCallback | 强制回调函数 |
- 第五个参数ValidateValueCallback
public delegate bool ValidateValueCallback(object value);
数据验证回调函数
使用
// 注册
ValueProperty = DependencyProperty.Register(
"Value",
typeof(int),
typeof(MainWindow),
new PropertyMetadata(
1,
new PropertyChangedCallback(OnPropertyChanged),// 第一个值回调位置
new CoerceValueCallback(OnCoerceValueCallback)// 第三个强制回调位置
),
new ValidateValueCallback(OnValidateValueCallBack)//第二个值验证回调位置
);
属性变化回调
/// <summary>
/// 属性值变化回调
/// 当设置属性的值时,第3个被触发
/// </summary>
/// <param name="d">属性所在的对象</param>
/// <param name="e">变化动作中所关联数据,oldValue,newValue等</param>
static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
验证回调
先调用验证回调,验证通过后,才调用属性变化回调。但是如果新旧值相同时,不触发属性变化回调
/// <summary>
/// 属性值验证回调
/// 当设置属性的值时,第1个被触发
/// </summary>
/// <param name="obj">属性将要写的入值</param>
/// <returns>是否允许写入</returns>
static bool OnValidateValueCallBack(object obj)
{
//if (int.Parse(obj.ToString()) > 1000)
// return false;
// 绑定的时候可以把异常捕获到进行提示
return true;
}
强制回调
首先,走验证回调,再走强制回调,再走属性变化回调
/// <summary>
/// 当设置属性的值时,第2个被触发
/// </summary>
/// <param name="d">属性所在的对象</param>
/// <param name="obj">当前属性的最新值</param>
/// <returns>希望属性可以接收的值</returns>
static object OnCoerceValueCallback(DependencyObject d, object obj)
{
if (int.Parse(obj.ToString()) > 1000)
return 1000;
return obj;
}
FrameworkPropertyMetadata参数
有时会使用FrameworkPropertyMetadata类作为DependencyProperty.Register
的第4个参数,该类继承自PropertyMetadata
public FrameworkPropertyMetadata(object defaultValue, FrameworkPropertyMetadataOptions flags, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback, bool isAnimationProhibited, UpdateSourceTrigger defaultUpdateSourceTrigger);
- FrameworkPropertyMetadataOptions:指定了与布局或者数据绑定等依赖项属性的交互特征
- isAnimationProhibited:这个属性能否用于动画
- defaultUpdateSourceTrigger:何时更新绑定元数据
属性继承
如果在<Window>
定义font=30,则在Window中的控件字体都会默认实现font=30,这是如何实现的呢?
class Control1:ContentControl
{
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
//注意要使用new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.Inherits)
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(Control1), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.Inherits));
}
class Control2 : ContentControl
{
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
//不再使用DependencyProperty.Register,而是使用Control1.ValueProperty.AddOwner
public static readonly DependencyProperty ValueProperty = Control1.ValueProperty.AddOwner(typeof(Control2),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.Inherits));
//也可以在Control1中使用Control1.ValueProperty.AddOwner()
}
<!--和顺序无关-->
<StackPanel>
<local:Control1 x:Name="c1_1" Value="10">
<local:Control2 x:Name="c1_2"/>
</local:Control1>
<TextBlock Text="{Binding Path=Value,ElementName=c1_2}"/>
<local:Control2 x:Name="c2_2" Value="10">
<local:Control1 x:Name="c2_1"/>
</local:Control2>
<TextBlock Text="{Binding Path=Value,ElementName=c2_1}"/>
</StackPanel>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-coXovJtm-1671794766265)(依赖属性.assets/image-20221211131925529.png)]
依赖附加属性
class Control3
{
public static string GetPasswordValue(DependencyObject obj)
{
return (string)obj.GetValue(PasswordValueProperty);
}
public static void SetPasswordValue(DependencyObject obj, string value)
{
obj.SetValue(PasswordValueProperty, value);
}
public static readonly DependencyProperty PasswordValueProperty =
DependencyProperty.RegisterAttached("PasswordValue", typeof(string), typeof(Control3), new PropertyMetadata("", new PropertyChangedCallback(OnPasswordValueChanged)));
private static void OnPasswordValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//第一个参数是使用该附加属性的对象
(d as PasswordBox).Password = e.NewValue.ToString();
}
}
<Window.Resources>
<sys:String x:Key="pwd">123456</sys:String>
</Window.Resources>
<StackPanel>
<PasswordBox Password="" local:Control3.PasswordValue="{Binding Source={StaticResource pwd}}"/>
</StackPanel>
PasswordBox中的Password属性是一个普通的string类型属性,无法使用Binding进行绑定,所以使用附加属性
类型转换
用于将XAML中输入的属性值(字符串)转换成对应的对象
1.自定义对象类
[TypeConverter(typeof(CPTypeConverter))] //使用转换类
public class ContorlProperty
{
public double Width { get; set; }
public double Height { get; set; }
}
- 使用ContorlProperty类型作为属性
public class Control4:ContentControl
{
public ContorlProperty MyProperty
{
get { return (ContorlProperty)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(ContorlProperty), typeof(Control4), new PropertyMetadata(null));
}
- 定义转换
public class CPTypeConverter : TypeConverter
{
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
//"100,60"
var temp = value.ToString().Split(',');
double v1 = double.Parse(temp[0]);
double v2 = double.Parse(temp[1]);
return new ContorlProperty() { Width = v1, Height = v2 };
}
}
- 完成上述步骤后可以这样使用
<StackPanel>
<local:Control4 MyProperty="100,60"/>
</StackPanel>
绑定表达式
如果要做数据变化通知,推荐使用INotifyPropertyChanged,而不是使用依赖属性,原因:
- 类可以不继承自DependencyObject
- 依赖属性是静态的,占用资源更大