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

依赖属性

依赖属性回调方法与参数

具有依赖属性的类必须继承自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; }
}
  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
  • 依赖属性是静态的,占用资源更大
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步、步、为营

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值