ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【WPF】依赖属性

2022-05-28 10:02:06  阅读:160  来源: 互联网

标签:依赖 DependencyProperty public DependencyObject WPF 属性


依赖属性DependencyProperty的诞生背景

WPF开发中,必须使用依赖对象作为依赖属性的宿主,使二者结合起来。依赖对象的概念被DependencyObject类所实现,依赖属性的概念则由DependencyProperty类所实现

WPF框架的编程经常和界面打交道,经常遇到的一个情况是某个属性的值的变化会影响到多个其他对象。比如当一个Button的改变大小超过了它的容器,他的容器应该自动调整大小。于是我们考虑在每个属性的set方法中触发一些事件,但很快我们发现现有的功能很难满足我们的需求,至少不能简洁漂亮的满足这些需求。
实际上我们的需求更加复杂,WPF中的数据绑定,XAML语法等很多地方都和属性密切相关,我们迫切需要一种功能更加强大的属性。

于是在WPF中,引入了一种特殊的属性,Dependency Property。这种属性和普通的属性最大不同在于,它的值的来源并不单一。对这种属性的取值和赋值都会能与其他对象有影响,因此能得到很大的灵活性

依赖属性和依赖对象

DependencyObject和DependencyPorperty两个类是WPF属性系统的核心。
在WPF中,依赖对象的概念被DependencyObject类实现;依赖属性的概念则由DependencyPorperty类实现。
必须使用依赖对象作为依赖属性的宿主,二者结合起来,才能实现完整的Binding目标被数据所驱动。DependencyObject具有GetValue和SetValue两个方法,用来获取/设置依赖属性的值。
DependencyObject是WPF系统中相当底层的一个基类,如下:

 

 

 继承树上可以看出,WPF的所有UI控件都是依赖对象。

依赖属性系统

 赖项属性的应用

 WPF中各个功能的实现都离不开依赖项属性的支持,如绑定,模板,动画,属性值的优先级

 动态资源——Background="{DynamicResource MyBrush}"

数据绑定——Background="{Binding MyBrush}"

样式——<Setter Property="Background" Value="Red"/>

动画——<ColorAnimation Storyboard.TargetName="MyButton" Storyboard.TargetProperty="Background" From="Red" To="Blue"/>

元数据重写——通过依赖属性的OverrideMetadata来重写父类的属性默认行为

属性值继承——继承元素树父级的属性值(DataContext,FontSize)

WPF设计器集成——使用WPF设计器时,可以在属性窗口编辑属性值

命名规则:

字段总是与属性同名,但其后面追加了 Property 后缀。 有关此约定及其原因的详细信息,

 

依赖属性创建

1、输入快捷键 "Propdp" 点击 TAB 按键自动生成默认 依赖属性如下为系统默认依赖属性

public int MyProperty
{
   get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
      DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

按照自己的需要修改依赖属性名称、属性名称。
依赖属性实例并非使用new操作符得到而是使用DependencyProperty.Register方法生成。

DependencyProperty.Register的参数说明

    • 第一个参数为int类型,表示指明以哪个CLR属性作为这个依赖属性的包装器。就是代码"MyProperty"
    • 第二个参数指明此依赖属性用来存储什么样的值。
    • 第三个参数用来指明此依赖属性的宿主是什么类型,或者说DependencyProperty.Register方法要将这个依赖属性注册到哪个类型上
  • 1.依赖属性包装器是一个CLR属性,并不是依赖属性,没有包装器,依赖属性依旧存在。
  • 2.既然没有包装器依赖属性也存在,那么包装器是干什么用的呢?包装器的作用是以"实例属性"的形式向外界暴露依赖属性,这样,一个依赖属性才能成为数据源的一个Path。
  • 3.注册依赖属性时使用的第二个参数是一个数据类型,这个数据类型也是包装器的数据类型。

    DependencyProperty.Register带4个参数,第四个参数的类型是PropertyMetadata类,作用是给依赖属性的DefaultMetadata属性赋值。顾名思义,DefaultMetadata的作用就是向依赖属性的调用者提供一些基本信息,这些信息包括:

  • CoerceValueCallback:依赖属性的值被强制改变时此委托会被调用,此委托可关联一个函数。
  • DefaultValue:依赖属性未被显示赋值时,若读取之则获得此默认值,不设置此值会抛出异常。
  • IsSealed:控制PropertyMetadata的属性值是否可以更改,默认值为true。
  • PropertyChangeCallback:依赖属性的值被改变之后此委托会被调用,此委托可关联一个函数。

定义

  上面介绍了依赖属性所带来的好处,这时候,问题又来了,怎样自己定义一个依赖属性呢?C#属性的定义大家再熟悉不过了。下面通过把C#属性进行改写成依赖属性的方式来介绍依赖属性的定义。下面是一个属性的定义:

1 public class Person
2     {
3         public string Name { get; set; }
6     }

在属性系统中注册属性

为使属性成为依赖属性,必须在属性系统维护的表中注册该属性,并为属性指定一个唯一标识符。此唯一标识符会用作后续属性系统操作的限定符。 这些操作可能是内部操作,也可能使用你自己的代码调用属性系统 API。 若要注册属性,可在类的主体内(在类中但在所有成员定义外)调用 Register 方法。 Register 方法调用也会提供标识符字段作为返回值。 Register 调用在其他成员定义外完成的原因在于,需要使用此返回值分配并创建一个 DependencyProperty 类型的 public static readonly 字段,作为类的一部分。 此字段会作为依赖属性的标识符。

 在把上面属性改写为依赖属性之前,下面总结下定义依赖属性的步骤:

  1. 让依赖属性的所在类型继承自DependencyObject类。DependencyObject 类提供了GetValue、 SetValue方法
  2. 使用public static 声明一个DependencyProperty的变量,该变量就是真正的依赖属性。
  3. 提供一个依赖属性的包装属性,通过这个属性来完成对依赖属性的读写操作。

  根据上面的四个步骤,下面来把Name属性来改写成一个依赖属性,具体的实现代码如下所示:

 1. 使类型继承DependencyObject类
    public class Person : DependencyObject
    {
        // 2. 声明一个静态只读的DependencyProperty 字段 // 3. 注册定义的依赖属性 
        public static readonly DependencyProperty NameProperty=DependencyProperty.Register("Name", typeof(string), typeof(Person), new PropertyMetadata("Learning Hard",OnValueChanged)); 
       
      // 4. 属性包装器,通过它来读取和设置我们刚才注册的依赖属性 public void SetValue (DependencyProperty dp, object value);
        public string Name
        {
            get { return (string)GetValue(nameProperty); }
            set { SetValue(nameProperty, value); }
        }

        private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e)
        {
            // 当只发生改变时回调的方法
        }

    }

PropertyMetadata:依赖项属性元数据

第四个参数支持 PropertyMetadate 类型,同时它派生的UIPropertyMetadata类型
以及UIPropertyMetadata派生的FrameworkPropertyMetadata 在不同类型上实现的效果略有差异,此次提出一个典型

若依赖属性为ObservableCollection<T>的时候,必须要使用FrameworkPropertyMetadata作为参数,代码如下,给DefaultVaule、DefaultUpdateSourceTrigger 赋默认值。

        new FrameworkPropertyMetadata
                {
                    DefaultValue = new ObservableCollection<T>(),
                    DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });

 

 自定义控件实现依赖属性有如下三个注意点

  • 定义对象是int、string等类型的时候,可以使用PropertyMetadate作为触发参数
  • 若定义的依赖属性为ObservableCollection 则register 中的第四个参数应该调整为 FrameworkPropertyMetadata,并附上默认值。
  • XAML绑定 依赖属性,必须通过 RealtiveSource 实现绑定,不同能通DataContext Self 的形式,因为UserContrl 需要对后续数据源的输入,若使用DataContext Self 的形式,则无法实现数据更新

标签:依赖,DependencyProperty,public,DependencyObject,WPF,属性
来源: https://www.cnblogs.com/cdaniu/p/16318139.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有