说明
以下皆个人理解,如有所误,请大佬指明
简介
依赖属性是WPF中很常见的一种属性,也是很多功能实现的基础,比如:数据绑定、动画、样式等。根据WPF的结构类图得知,
DependencyObjective
类是所有依赖属性的基类,所以WPF中大部分控件中的属性基本都是依赖属性。由此,当自定义控件时就需要定义依赖属性。
如何定义
手动定义依赖属性
我们可以定义一个类,使其继承DependencyObject
,假设我们定义了一个用户类
public class UserInfo : DependencyObject
{
public static readonly DependencyProperty IdProperty = DependencyProperty.Register(
"Id", //属性名称,可以在XAML中调用
typeof(int), //属性类型
typeof(UserInfo) //属性所在的类
);
public int Id //属性的包装
{
get { return (int)GetValue(IdProperty); }
set { SetValue(IdProperty, value); }
}
}
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:UserInfo Id="{Binding }" /> <!--可使用UserInfo类中的Id属性-->
</Grid>
</Window>
在XAML中我们是无法使用的,原因就是继承的并不是UIElement
类,所以推荐继承UIElement
以下的类,修改被继承类后在XAML中使用,如果发现报错可以重新生成项目试一下。
快捷定义
在C#中输入propdp
并按两次TAB键可快速添加依赖属性
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name",
typeof(string),
typeof(UserInfo),
new PropertyMetadata("") //属性默认值
);
依赖属性的回调
属性值改变回调
属性值改变时将触发的事件,可做一些业务逻辑
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name",
typeof(string),
typeof(UserInfo),
new PropertyMetadata("",new PropertyChangedCallback(OnNameValueChanged)) //绑定属性改变事件
);
private static void OnNameValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//业务逻辑
}
属性验证回调
属性每次赋值时都会触发这个事件,可用于验证数值的合法性
public static readonly DependencyProperty IdProperty = DependencyProperty.Register(
"Id", //属性名称,可以在XAML中调用
typeof(int), //属性类型
typeof(UserInfo), //属性所在的类
new PropertyMetadata(0),
new ValidateValueCallback(OnIdValidate) //绑定OnIdValidate事件
);
public int Id //属性的包装
{
get { return (int)GetValue(IdProperty); }
set { SetValue(IdProperty, value); }
}
private static bool OnIdValidate(object value)
{
return (int)value > 0; //验证Id这个属性的新值是否大于0
}
属性强制回调
强制回调就是调用属性时每次都会强制触发的事件,使用时需要在注册依赖属性时添加委托,而且必须有属性改变回调
public static readonly DependencyProperty IdProperty = DependencyProperty.Register(
"Id", //属性名称,可以在XAML中调用
typeof(int), //属性类型
typeof(UserInfo), //属性所在的类
new PropertyMetadata(0, OnIdChanged, OnIdCoerce), //属性改变回调,强制回调
new ValidateValueCallback(OnIdValidate) //验证回调
);
private static bool OnIdValidate(object value)
{
return (int)value > 0;
}
private static object OnIdCoerce(DependencyObject d, object baseValue)
{
if ((int)baseValue < 0)
return 0; // 强制值为非负数
else
return (int)baseValue;
}
private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//业务逻辑
}
三种回调都可做一些业务,而属性改变回调用的较多
属性的继承
属性继承和类的继承类似,子属性的值会随着父属性的值改变而改变
如何定义
在注册依赖属性时,添加FrameworkPropertyMetadata
参数,并在参数中选择继承,此时UserInfo
中的Name属性作为父属性
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name",
typeof(string),
typeof(UserInfo),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.Inherits) //继承
);
再定义一个类,让其中的依赖属性继承上面的父属性,只需要使用父属性的AddOwner
方法就行
public class TestInherit : UIElement
{
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty = UserInfo.NameProperty.AddOwner(
typeof(TestInherit),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.Inherits)
);
}
附加属性
附加属性是一种特殊的依赖属性,允许继承自DependencyObject
任何XAML元素设置额外的属性
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--Grid.Row并不是TextBlock中的属性,它是一个附加属性-->
<TextBlock Grid.Row="0"
Text="Hello WPF"
FontSize="80" />
</Grid>
定义附加属性
使用快捷输入propa
可快速定义附加属性
public class TextAttach
{
public static string GetCusPWD(DependencyObject obj)
{
return (string)obj.GetValue(CusPWDProperty);
}
public static void SetCusPWD(DependencyObject obj, string value)
{
obj.SetValue(CusPWDProperty, value);
}
public static readonly DependencyProperty CusPWDProperty =
DependencyProperty.RegisterAttached(
"CusPWD",
typeof(string),
typeof(TextAttach),
new PropertyMetadata("")
);
}
可以在XAML中使用这个附件属性,注意添加一下命名空间
<TextBlock
Grid.Row="0"
FontSize="80"
Text="Hello WPF"
local:TextAttach.CusPWD="Test" />
使用了TextAttach
类中CusPWD
属性,此属性可以绑定。因此附加属性可以应用在为一些密封的控件增加属性的场景,比如PasswordBox
这个控件,出于安全性考虑,其中的Password
属性不能绑定,但是我们可以通过附加属性来进行操作。
Password的双向绑定
我们定义出两个附加属性CusPWD
和IsSub
,CusPWD
是自定义的密码附件属性,可与实体对象进行数据绑定,IsSub
是自定义的密码绑定开关,意思是是否开启密码绑定。
public static string GetCusPWD(DependencyObject obj)
{
return (string)obj.GetValue(CusPWDProperty);
}
public static void SetCusPWD(DependencyObject obj, string value)
{
obj.SetValue(CusPWDProperty, value);
}
public static readonly DependencyProperty CusPWDProperty =
DependencyProperty.RegisterAttached(
"CusPWD",
typeof(string),
typeof(TextAttach),
new PropertyMetadata("", OnCusPWDValueChanged)
);
private static void OnCusPWDValueChanged( //属性值改变事件OnCusPWDValueChanged
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
PasswordBox pb = d as PasswordBox;
//这里的d表示依赖对象,即这个附加属性在哪个控件对象中使用,d就表示这个控件对象
if (pb != null && e.OldValue != e.NewValue)
{
pb.Password = e.NewValue as string;
}
}
public static bool GetIsSub(DependencyObject obj)
{
return (bool)obj.GetValue(IsSubProperty);
}
public static void SetIsSub(DependencyObject obj, bool value)
{
obj.SetValue(IsSubProperty, value);
}
public static readonly DependencyProperty IsSubProperty =
DependencyProperty.RegisterAttached(
"IsSub",
typeof(bool),
typeof(TextAttach),
new PropertyMetadata(false, OnIsSubChanged)
);
private static void OnIsSubChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PasswordBox pb = d as PasswordBox;
pb.PasswordChanged -= Pb_PasswordChanged; //防止订阅多个Pb_PasswordChanged事件
if ((bool)e.NewValue == true) //属性值只有从false变为true时才会订阅
pb.PasswordChanged += Pb_PasswordChanged;
}
private static void Pb_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox pb = sender as PasswordBox;
if (GetCusPWD(pb) != pb.Password)
SetCusPWD(pb, pb.Password);
}
~~~
Value == true) //属性值只有从false变为true时才会订阅
pb.PasswordChanged += Pb_PasswordChanged;
}
private static void Pb_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox pb = sender as PasswordBox;
if (GetCusPWD(pb) != pb.Password)
SetCusPWD(pb, pb.Password);
}