浅析WPF之依赖属性

代码星辉·七月创作之星挑战赛 10w+人浏览 231人参与

说明

以下皆个人理解,如有所误,请大佬指明

简介

依赖属性是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的双向绑定

我们定义出两个附加属性CusPWDIsSubCusPWD是自定义的密码附件属性,可与实体对象进行数据绑定,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);
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值