依赖属性

依赖属性回调方法与参数

具有依赖属性的类必须继承自​​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:指定了与布局或者数据绑定等依赖项属性的交互特征

WPF依赖属性、附加属性、属性继承、类型转换详解_Parse

  • isAnimationProhibited:这个属性能否用于动画
  • defaultUpdateSourceTrigger:何时更新绑定元数据

WPF依赖属性、附加属性、属性继承、类型转换详解_微软_02

属性继承

如果在​​<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>

WPF依赖属性、附加属性、属性继承、类型转换详解_wpf_03

依赖附加属性

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; }
}
  1. 使用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));
}
  1. 定义转换
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 };
}
}
  1. 完成上述步骤后可以这样使用
<StackPanel>
<local:Control4 MyProperty="100,60"/>
</StackPanel>

绑定表达式

如果要做数据变化通知,推荐使用INotifyPropertyChanged,而不是使用依赖属性,原因:

  • 类可以不继承自DependencyObject
  • 依赖属性是静态的,占用资源更大