DependencyObject和DependencyPorperty两个类是WPF属性系统的核心。

在WPF中,依赖对象的概念被DependencyObject类实现;依赖属性的概念则由DependencyPorperty类实现。

必须使用依赖对象作为依赖属性的宿主,二者结合起来,才能实现完整的Binding目标被数据所驱动。DependencyObject具有GetValue和SetValue两个方法,用来获取/设置依赖属性的值。

DependencyObject是WPF系统中相当底层的一个基类,如下:

从这颗继承树可以看出,WPF的所有UI控件都是依赖对象。WPF的类库在设计时充分利用了依赖属性的优势,UI空间的饿绝大多数属性都已经依赖化了。

下面用一个简单的实例来说明依赖属性的使用方法。先准备好一个界面,顺便复习下前面的Style和Template:

  1. <Window x:Class="DependencyObjectProperty.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="MainWindow" Height="" Width="">
  5. <Window.Resources>
  6. <Style x:Key="textStyle" TargetType="{x:Type TextBox}">
  7. <Setter Property="Template">
  8. <Setter.Value>
  9. <ControlTemplate TargetType="{x:Type TextBox}">
  10. <TextBlock Background="CadetBlue" Foreground="HotPink" Text="{TemplateBinding Property=Text}"/>
  11. </ControlTemplate>
  12. </Setter.Value>
  13. </Setter>
  14.  
  15. <Style.Triggers>
  16. <Trigger Property="IsMouseOver" Value="true">
  17. <Setter Property="Template">
  18. <Setter.Value>
  19. <ControlTemplate TargetType="{x:Type TextBox}">
  20. <Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
  21. <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Background="AliceBlue"/>
  22. </Border>
  23. </ControlTemplate>
  24. </Setter.Value>
  25. </Setter>
  26. </Trigger>
  27. </Style.Triggers>
  28. </Style>
  29. </Window.Resources>
  30. <StackPanel>
  31. <TextBox Style="{StaticResource textStyle}" Height="" Name="textBox1" FontSize="" Margin="" Width="" />
  32. <TextBox Style="{StaticResource textStyle}" Height="" Name="textBox2" FontSize="" Margin="" Width="" />
  33. <Button Content="Button" Height="" Name="button1" Width="" Click="button1_Click" />
  34. </StackPanel>
  35. </Window>

前面说过,DependencyProperty必须以DependencyObject为宿主、借助它的SetValue和GetValue方法进行写入和读取。因此,想用自定义的DependencyProperty,宿主一定是DependencyObject的派生类。

DependencyProperty实例的声明特点很明显:变量由public static readonly三个修饰符修饰,实例使用DependencyProperty.Register方法生成。而非new操作符得到。

代码如下:

  1. using System.Windows;
  2.  
  3. namespace DependencyObjectProperty
  4. {
  5. class Student:DependencyObject
  6. {
  7. //定义依赖属性
  8. public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
  9. }
  10. }

这是自定义DependencyProperty的最简单代码。
依赖属性也是属性,下面来使用它:

  1. private void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3. Student stu = new Student();
  4. stu.SetValue(Student.NameProperty, textBox1.Text);
  5. textBox2.Text = (string)stu.GetValue(Student.NameProperty);
  6. }

在textBox1中输入值,点下Button1后效果如下:

上面我们使用的依赖属性是靠GetValue和SetValue进行对外的暴露,而且在GetValue的时候需要进行类型的转换,因此,在大多数的情况下我们会为依赖属性添加一个CLR属性的外包装:

  1. using System.Windows;
  2.  
  3. namespace DependencyObjectProperty
  4. {
  5. class Student:DependencyObject
  6. {
  7. //CLR属性进行封装
  8. public string Name
  9. {
  10. get { return (string)GetValue(NameProperty); }
  11. set { SetValue(NameProperty, value); }
  12. }
  13.  
  14. //定义依赖属性
  15. public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
  16. }
  17. }

有了这个CLR属性包装,我们就可以和CLR属性一样访问依赖属性了:

  1. private void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3. Student stu = new Student();
  4. stu.Name = textBox1.Text;
  5. textBox2.Text = stu.Name;
  6. }

如果不关心底层的实现,下游的程序员在使用依赖属性时与使用单纯的CLR属性别无二致。

效果和上面相同:

当然如果不用Binding,依赖属性的设计就没有意义,下面我们使用Binding把Student对象关联到textBox1上,再把textBox2关联到Student对象上。代码如下:

  1. private void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3. Student stu = new Student();
  4. Binding binding = new Binding("Text") { Source = textBox1 };
  5. BindingOperations.SetBinding(stu, Student.NameProperty, binding);
  6.  
  7. Binding binding2 = new Binding("Name") { Source = stu };
  8. BindingOperations.SetBinding(textBox2, TextBox.TextProperty, binding2);
  9. }

当然第二个Binding也可以这样写,下面两者等效:

  1. Binding binding2 = new Binding("Name") { Source = stu };
  2. BindingOperations.SetBinding(textBox2, TextBox.TextProperty, binding2);
  1. textBox2.SetBinding(TextBox.TextProperty, binding2);

也可以在Student类中封装FrameworkElement类的SetBinding方法,如下:

  1. using System.Windows;
  2. using System.Windows.Data;
  3.  
  4. namespace DependencyObjectProperty
  5. {
  6. class Student:DependencyObject
  7. {
  8. //CLR属性进行封装
  9. public string Name
  10. {
  11. get { return (string)GetValue(NameProperty); }
  12. set { SetValue(NameProperty, value); }
  13. }
  14.  
  15. //定义依赖属性
  16. public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
  17.  
  18. //SetBinding包装
  19. public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
  20. {
  21. return BindingOperations.SetBinding(this, dp, binding);
  22. }
  23. }
  24. }

则Binding可进一步写成这样:

  1. private void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3. Student stu = new Student();
  4. stu.SetBinding(Student.NameProperty, new Binding("Text") { Source=textBox1 });
  5. textBox2.SetBinding(TextBox.TextProperty, new Binding("Name") { Source=stu});
  6. }

效果如下:

//---------------------------------------------------------

自定义依赖属性也可以不需要手动进行声明、注册并使用CLR属性进行封装,只需要输入propdp,同时连按两次Tab,一个标准的依赖属性(带CLR属性包装)就声明好了。

prop:CLR属性

propa:附加属性

propdp:依赖属性

附加属性也是一种特别的依赖属性,顾名思义,附加属性是说一个属性本来不属于某个对象,但是由于某种需求而被后来附加上。也就是把对象放入一个特定的环境后对象才具有的属性,比如Canvas.Left DockPanel.Dock Grid.Column等。

声明时一样用public static readonly三个关键词修饰。唯一不同就是注册附加属性使用的是名为RegisterAttached的方法,但参数与Register方法相同。附加属性的包装器也与依赖属性不同,依赖属性使用CLR属性对GetValue和SetValue两个方法进行包装,附加属性则使用两个方法分别进行包装。

其可由propa+tab+tab方便的生成。理解附加属性的意义及使用场合即可。

WPF 自定义依赖属性的更多相关文章

  1. WPF自定义依赖集合属性无法触发更新的问题

    通常WPF中通过继承UserControl的来快速创建自定义控件,最近项目上需要设计一个卫星星图显示控件,最终效果如下图所示.完成过程中遇到了自定义集合依赖属性无法触发更新通知的问题,在此记录一下,方 ...

  2. WPF 中依赖属性的继承(Inherits)

    WPF中依赖属性的值是是可以设置为可继承(Inherits)的,这种模式下,父节点的依赖属性会将其值传递给子节点.例如,数据绑定中经常使用的DataContextProperty: var host ...

  3. WPF 使用依赖属性(DependencyProperty) 定义用户控件中的Image Source属性

    原文:WPF 使用依赖属性(DependencyProperty) 定义用户控件中的Image Source属性 如果你要自定义一个图片按钮控件,那么如何在主窗体绑定这个控件上图片的Source呢? ...

  4. WPF的依赖属性和附加属性(用法解释较全)

    转:https://www.cnblogs.com/zhili/p/WPFDependencyProperty.html 一.引言 感觉最近都颓废了,好久没有学习写博文了,出于负罪感,今天强烈逼迫自己 ...

  5. WPF 之 依赖属性与附加属性(五)

    一.CLR 属性 ​ 程序的本质是"数据+算法",或者说用算法来处理数据以期得到输出结果.在程序中,数据表现为各种各样的变量,算法则表现为各种各样的函数(操作符是函数的简记法). ...

  6. WPF的依赖属性

    Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR)属性的功能,这些服务通常统称为 WPF 属性系统.由 WPF 属 ...

  7. wpf 的依赖属性只能在loaded 事件之后才能取到

    wpf 的依赖属性只能在loaded 事件之后才能取到,在构造函数的  InitializeComponent(); 之后取不到 wpf 的依赖属性只能在loaded 事件之后才能取到,在构造函数的  ...

  8. WPF 用户控件的自定义依赖属性在 MVVM 模式下的使用备忘

    依赖属性相当于扩充了 WPF 标签的原有属性列表,并可以使用 WPF 的绑定功能,可谓是十分方便的:用户控件则相当于代码重用的一种方式:以上几点分开来还是比较好理解的,不过要用到MVVM 模式中,还是 ...

  9. WPF usercontrol 自定义依赖属性

    1.依赖属性不同意一般属性,一般属性主要定义在对象中,而依赖属性是存在一个特殊的依赖属性表中.2.当我们触发改变值时,需要通过SetValue这种方式进行触发. UserControl1.xaml: ...

随机推荐

  1. Python知识点复习之__call__

    一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用.能不能直接在实例本身上调用呢?在Python中,答案是肯定的. 任何类,只需要定义一个__ca ...

  2. 自定义ActionBar图标

    <style name="Theme.glTheme" parent="android:Theme.Holo"> <item name=&qu ...

  3. 解决python中write()函数向文件中写中文时出现乱码的问题

    今天看<python编程从入门到实践>的第10章文件.异常,在做练习的时候,向文件中写内容,但是写中文就不行,后来在百度上查了众多资料,解决方法如下: 解决:在open()函数中添加一个e ...

  4. linux c编程:互斥锁条件变量

    条件变量:等待与信号发送 使用互斥锁虽然可以解决一些资源竞争的问题,但互斥锁只有两种状态(加锁和解锁),这限制了互斥锁的用途. 条件变量(条件锁)也可以解决线程同步和共享资源访问的问题,条件变量是对互 ...

  5. 学点TCPDUMP

    [root@future ~]# yum install tcpdump 官网地址: https://nmap.org/ 还有中文手册,太感动了 https://nmap.org/man/zh/man ...

  6. 3 TensorFlow入门之识别手写数字

    ------------------------------------ 写在开头:此文参照莫烦python教程(墙裂推荐!!!) ---------------------------------- ...

  7. 第四课 Makefile文件的制作(下)

    1序言: 前面一节课讲解了Makefile的基础知识包括原理.预定义以及命令格式,这样是可以完成一个自动编译的文件,这些知识可以帮你完成.想想mak真是强大啊,可能有些同志发现了如果项目文件太多每个目 ...

  8. NoSQL2

    系统的可扩展性是推动NoSQL运动发展的的主要理由,包含了分布式系统协调,故障转移,资源管理和许多其他特性.这么讲使得NoSQL听起来像是一个大筐,什么都能塞进去.尽管NoSQL运动并没有给分布式数据 ...

  9. Wireshark(一):Wireshark基本用法

    转载:https://community.emc.com/message/818739#818739 按照国际惯例,从最基本的说起. 抓取报文: 下载和安装好Wireshark之后,启动Wiresha ...

  10. JavaScript的消息机制

    JavaScript本身是单线程的,但它却是事件驱动的.类似Windows窗体应用程序,它也需要消息队列机制来实现.程序的执行并不是连续的,绝大多数时间都在等待消息.每次执行执行程序都是在响应消息,这 ...