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

news2024/11/18 23:23:07

依赖属性

依赖属性回调方法与参数

具有依赖属性的类必须继承自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:指定了与布局或者数据绑定等依赖项属性的交互特征
image-20221211124538789
  • isAnimationProhibited:这个属性能否用于动画
  • defaultUpdateSourceTrigger:何时更新绑定元数据
image-20221211125410896

属性继承

如果在<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
  • 依赖属性是静态的,占用资源更大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111039.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国家“数据安全三认证”图解来了

二十大指引新时代新征程。蓝图已经绘就&#xff0c;号角已经吹响&#xff0c;新征程是充满光荣和梦想的远征。二十大报告深刻阐明了实现中华民族伟大复兴的一系列重大问题&#xff0c;系统擘画了以中国式现代化推进民族复兴的宏伟蓝图&#xff0c;是引领中华民族伟大复兴的政治…

论文导读 | GPU上的动态图数据管理技术浅析

一、背景介绍 随着相关技术的发展&#xff0c;图计算与分析系统在大量场景中得到应用。同时&#xff0c;为了解决图规模巨大等因素导致的性能下降问题&#xff0c;研究人员利用GPU这一新型计算硬件&#xff0c;设计了大量高效的图计算与分析解决方案。GPU提供的高并发计算能力…

我司何晓磊受邀参加东盟与中日韩中小企业人工智能产业论坛并担任评委

我司何晓磊受邀参加东盟与中日韩中小企业人工智能产业论坛并担任评委 一、活动背景 为推动东盟与中日韩&#xff08;103&#xff09;中小企业服务联盟务实合作&#xff0c;帮助中小企业提高生产力和技术创新能力&#xff0c;进一步提高国际化发展水平&#xff0c;促进东亚区域…

C++——哈希练习题

文章目录一、编程题1.在长度 2N 的数组中找出重复 N 次的元素2. 两个数组的交集二、面试题给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。【腾讯】&#xff08;一&#xff09;位图应用1. 给定100亿个整…

C++ :STL:初识

1&#xff1a;STL初识 1.1 STL的诞生 STL 诞生来源 长久依赖&#xff0c;软件界一直希望建立一种可重复利用的东西C 的面向对象和泛型思想&#xff0c;目的就是复用性的提升大多数情况下&#xff0c;数据结构和算法都未能有一套标准&#xff0c;导致被迫从事大量重复的工作为了…

多线程问题(一)

目录 一、为什么引入线程&#xff1f; 二、线程和进程的区别 三、创建线程的五种方式 1、创建类继承Thread类 2、创建类实现Runnable接口 3、构造Thread类的匿名内部类 4、构造Runnable的匿名内部类 5、使用lambda表达式 四、start方法与run方法的区别 五、线程…

Promise对象的使用

一、什么是Promise Promise 是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;回调函数和事件&#xff09;更合理和更强大。从语法上说&#xff0c;Promise 是一个对象&#xff0c;从它可以获取异步操作的消息。Promise 提供统一的 API&#xff0c;各种异步操…

攻略丨在小红书高效种草,品牌要问的7个问题

这个圣诞节&#xff0c;小羊人和冬阴功&#xff08;这个冬天依然阴着的打工人&#xff09;们各怀心事&#xff0c;最有圣诞氛围的地方要数小红书了。打开首页就能看到&#xff0c;宅家自制光影圣诞树&#xff0c;被安利好利来蛋糕&#xff0c;再往下滑一滑&#xff0c;还有圣诞…

[开源工具]2022免费临时邮箱(Temp Free Mail)

2022免费临时邮箱Temp Free Mail1、10分钟邮箱2、45分钟邮箱3、60分钟邮箱4、24小时邮箱5、5日邮箱6、其他临时邮箱7、无时间限制临时邮箱8、临时邮箱常见问题答疑临时邮箱&#xff0c;英文名称 Temp Mail&#xff0c;也被称为一次性邮箱或匿名邮箱&#xff0c;根据它的邮件有效…

Vue - npm 批量升级依赖包

参考&#xff1a; npm 如何更新项目最新依赖包 一行命令更新所有 npm 依赖包 npm 升级依赖包 批量升级有风险&#xff01;&#xff01;&#xff01;升级需谨慎&#xff01;&#xff01; 常规的包升级方式 npm update (包) 检查项目可升级的包 方式一 该命令将检查每个已安装的…

初识C++ - 类与对象(下篇·下)

目录 再谈构造函数 隐式类型的转换 explicit关键字 单参数 多参数 static静态 一道关于static的题目 友元 友元函数 友元类 内部类 匿名对象 拷贝对象时的一些编译器优化 结束语 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函…

服装进销存管理软件哪个比较好用?

做好库存是服装行业是保障店铺正常运营重要方面。如果只是靠人工清点记录服装库存情况、手工记账&#xff0c;会花费大量的人员和精力&#xff0c;还不能保证一定的效率和准确率。而且服装业具有鲜明的行业特性&#xff1a;服装款式多、季节性强、颜色/尺码等等&#xff0c;如果…

Influxdb双写服务influxdb-relay部署配置【离线】

Background Influxdb社区版未提供集群方案&#xff0c;官方提供的集群模式为闭源收费版本&#xff0c;具体收费明细不太清楚哈&#xff0c;有知道的请留言告知哈。官方开源的influxdb-relay仅仅支持双写功能&#xff0c;并未支持负载均衡能力&#xff0c;仅仅解决了数据备份的问…

【C++初阶】友元(友元函数友元类)、内部类、匿名对象、拷贝对象时的优化

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【C学习与应用】 ✒️✒️本篇内容&#xff1a;友元函数和友元类的概念和基础应用&#xff0c;简单介绍内部类、匿名对象、拷贝对象时的部分编译器优化情况…

Java守护线程简述

Java守护线程简述前言前置知识线程JVM退出代码测试查看子线程是否继承父线程的类型守护线程在程序退出时的表现普通线程在程序退出时的表现总结前言 最近再看《Java并发编程实战》&#xff0c;正好有一小节关于守护线程的知识&#xff0c;这里做一点小总结。 前置知识 这里只…

云原生之Dockerfile简介和基础实践

dockerfile简介和基础实践一、Dockerfile简介1.1、Dockerfile解决的问题1.2、docker build 构建流程1.3、关键字介绍二、Dockerfile 实践2.1、基本语法实践 --- golang问题检查2.2、基本语法实践 --- gcc总结后言一、Dockerfile简介 Dockerfile是一个创建镜像所有命令的文本文…

为行业赋能 助力行业客户业务大放异彩

近日&#xff0c;2022亚马逊云科技re:Invent全球大会已完美落幕&#xff0c;在大会上发布了很多重磅新品&#xff0c;包括云原生数据战略、硬件创新、高性能计算等等在各行各业中的创新应用&#xff0c;下面就来看看医疗与生命科学、市场调研和数据分析、汽车行业&#xff0c;他…

如何理解UML2.5.1(04篇)

第一步&#xff1a; 这里发现UML2.5.1中的一处错误&#xff1a; 图四、Figure9.10中的一处错误。 错误就在于最下面一个关联右端点处的标记redefines&#xff0c;有了这个标记&#xff0c;就应该意味着此关联特化了某个关联&#xff0c;但是如果我们用“A_ownedAttribute_class…

在Android端集成OpenCV的三种方式

1.Opencv Android SDK 基于Opencv C本地代码&#xff0c;通过Java语言接口使用JNI技术调用C本地方法的SDK开发包。 &#xff08;1&#xff09;etc:各类模型文件存储地址 &#xff08;2&#xff09;java:Java版本的Android SDK相关文件 &#xff08;3&#xff09;native:JNI层…

【QGIS入门实战精品教程】3.4:QGIS创建GeoPackage地理数据库及数据入库案例详解

GeoPackage(以下简称gpkg),内部使用SQLite实现的一种单文件、与操作系统无关的地理数据库。在QGIS中可以很方便的实现GeoPackage的创建与连接等操作。 一、QGIS创建GeoPackage 1. 创建数据库 QGIS创建GeoPackage的方法与ArcGIS中创建File GDB的类似,选择一个目标文件夹,…