WPF 自定义依赖属性
在WPF中,依赖对象的概念被DependencyObject类实现;依赖属性的概念则由DependencyPorperty类实现。
必须使用依赖对象作为依赖属性的宿主,二者结合起来,才能实现完整的Binding目标被数据所驱动。DependencyObject具有GetValue和SetValue两个方法,用来获取/设置依赖属性的值。
DependencyObject是WPF系统中相当底层的一个基类,如下:
从这颗继承树可以看出,WPF的所有UI控件都是依赖对象。WPF的类库在设计时充分利用了依赖属性的优势,UI空间的饿绝大多数属性都已经依赖化了。
下面用一个简单的实例来说明依赖属性的使用方法。先准备好一个界面,顺便复习下前面的Style和Template:

- <Window x:Class="DependencyObjectProperty.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow" Height="" Width="">
- <Window.Resources>
- <Style x:Key="textStyle" TargetType="{x:Type TextBox}">
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type TextBox}">
- <TextBlock Background="CadetBlue" Foreground="HotPink" Text="{TemplateBinding Property=Text}"/>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- <Style.Triggers>
- <Trigger Property="IsMouseOver" Value="true">
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type TextBox}">
- <Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
- <ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Background="AliceBlue"/>
- </Border>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Trigger>
- </Style.Triggers>
- </Style>
- </Window.Resources>
- <StackPanel>
- <TextBox Style="{StaticResource textStyle}" Height="" Name="textBox1" FontSize="" Margin="" Width="" />
- <TextBox Style="{StaticResource textStyle}" Height="" Name="textBox2" FontSize="" Margin="" Width="" />
- <Button Content="Button" Height="" Name="button1" Width="" Click="button1_Click" />
- </StackPanel>
- </Window>

前面说过,DependencyProperty必须以DependencyObject为宿主、借助它的SetValue和GetValue方法进行写入和读取。因此,想用自定义的DependencyProperty,宿主一定是DependencyObject的派生类。
DependencyProperty实例的声明特点很明显:变量由public static readonly三个修饰符修饰,实例使用DependencyProperty.Register方法生成。而非new操作符得到。
代码如下:

- using System.Windows;
- namespace DependencyObjectProperty
- {
- class Student:DependencyObject
- {
- //定义依赖属性
- public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
- }
- }

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

- private void button1_Click(object sender, RoutedEventArgs e)
- {
- Student stu = new Student();
- stu.SetValue(Student.NameProperty, textBox1.Text);
- textBox2.Text = (string)stu.GetValue(Student.NameProperty);
- }

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

- using System.Windows;
- namespace DependencyObjectProperty
- {
- class Student:DependencyObject
- {
- //CLR属性进行封装
- public string Name
- {
- get { return (string)GetValue(NameProperty); }
- set { SetValue(NameProperty, value); }
- }
- //定义依赖属性
- public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
- }
- }

有了这个CLR属性包装,我们就可以和CLR属性一样访问依赖属性了:
- private void button1_Click(object sender, RoutedEventArgs e)
- {
- Student stu = new Student();
- stu.Name = textBox1.Text;
- textBox2.Text = stu.Name;
- }
如果不关心底层的实现,下游的程序员在使用依赖属性时与使用单纯的CLR属性别无二致。
效果和上面相同:
当然如果不用Binding,依赖属性的设计就没有意义,下面我们使用Binding把Student对象关联到textBox1上,再把textBox2关联到Student对象上。代码如下:

- private void button1_Click(object sender, RoutedEventArgs e)
- {
- Student stu = new Student();
- Binding binding = new Binding("Text") { Source = textBox1 };
- BindingOperations.SetBinding(stu, Student.NameProperty, binding);
- Binding binding2 = new Binding("Name") { Source = stu };
- BindingOperations.SetBinding(textBox2, TextBox.TextProperty, binding2);
- }

当然第二个Binding也可以这样写,下面两者等效:
- Binding binding2 = new Binding("Name") { Source = stu };
- BindingOperations.SetBinding(textBox2, TextBox.TextProperty, binding2);
- textBox2.SetBinding(TextBox.TextProperty, binding2);
也可以在Student类中封装FrameworkElement类的SetBinding方法,如下:

- using System.Windows;
- using System.Windows.Data;
- namespace DependencyObjectProperty
- {
- class Student:DependencyObject
- {
- //CLR属性进行封装
- public string Name
- {
- get { return (string)GetValue(NameProperty); }
- set { SetValue(NameProperty, value); }
- }
- //定义依赖属性
- public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
- //SetBinding包装
- public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
- {
- return BindingOperations.SetBinding(this, dp, binding);
- }
- }
- }

则Binding可进一步写成这样:

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

效果如下:
//---------------------------------------------------------
自定义依赖属性也可以不需要手动进行声明、注册并使用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 自定义依赖属性的更多相关文章
- WPF自定义依赖集合属性无法触发更新的问题
通常WPF中通过继承UserControl的来快速创建自定义控件,最近项目上需要设计一个卫星星图显示控件,最终效果如下图所示.完成过程中遇到了自定义集合依赖属性无法触发更新通知的问题,在此记录一下,方 ...
- WPF 中依赖属性的继承(Inherits)
WPF中依赖属性的值是是可以设置为可继承(Inherits)的,这种模式下,父节点的依赖属性会将其值传递给子节点.例如,数据绑定中经常使用的DataContextProperty: var host ...
- WPF 使用依赖属性(DependencyProperty) 定义用户控件中的Image Source属性
原文:WPF 使用依赖属性(DependencyProperty) 定义用户控件中的Image Source属性 如果你要自定义一个图片按钮控件,那么如何在主窗体绑定这个控件上图片的Source呢? ...
- WPF的依赖属性和附加属性(用法解释较全)
转:https://www.cnblogs.com/zhili/p/WPFDependencyProperty.html 一.引言 感觉最近都颓废了,好久没有学习写博文了,出于负罪感,今天强烈逼迫自己 ...
- WPF 之 依赖属性与附加属性(五)
一.CLR 属性 程序的本质是"数据+算法",或者说用算法来处理数据以期得到输出结果.在程序中,数据表现为各种各样的变量,算法则表现为各种各样的函数(操作符是函数的简记法). ...
- WPF的依赖属性
Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR)属性的功能,这些服务通常统称为 WPF 属性系统.由 WPF 属 ...
- wpf 的依赖属性只能在loaded 事件之后才能取到
wpf 的依赖属性只能在loaded 事件之后才能取到,在构造函数的 InitializeComponent(); 之后取不到 wpf 的依赖属性只能在loaded 事件之后才能取到,在构造函数的 ...
- WPF 用户控件的自定义依赖属性在 MVVM 模式下的使用备忘
依赖属性相当于扩充了 WPF 标签的原有属性列表,并可以使用 WPF 的绑定功能,可谓是十分方便的:用户控件则相当于代码重用的一种方式:以上几点分开来还是比较好理解的,不过要用到MVVM 模式中,还是 ...
- WPF usercontrol 自定义依赖属性
1.依赖属性不同意一般属性,一般属性主要定义在对象中,而依赖属性是存在一个特殊的依赖属性表中.2.当我们触发改变值时,需要通过SetValue这种方式进行触发. UserControl1.xaml: ...
随机推荐
- Python知识点复习之__call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用.能不能直接在实例本身上调用呢?在Python中,答案是肯定的. 任何类,只需要定义一个__ca ...
- 自定义ActionBar图标
<style name="Theme.glTheme" parent="android:Theme.Holo"> <item name=&qu ...
- 解决python中write()函数向文件中写中文时出现乱码的问题
今天看<python编程从入门到实践>的第10章文件.异常,在做练习的时候,向文件中写内容,但是写中文就不行,后来在百度上查了众多资料,解决方法如下: 解决:在open()函数中添加一个e ...
- linux c编程:互斥锁条件变量
条件变量:等待与信号发送 使用互斥锁虽然可以解决一些资源竞争的问题,但互斥锁只有两种状态(加锁和解锁),这限制了互斥锁的用途. 条件变量(条件锁)也可以解决线程同步和共享资源访问的问题,条件变量是对互 ...
- 学点TCPDUMP
[root@future ~]# yum install tcpdump 官网地址: https://nmap.org/ 还有中文手册,太感动了 https://nmap.org/man/zh/man ...
- 3 TensorFlow入门之识别手写数字
------------------------------------ 写在开头:此文参照莫烦python教程(墙裂推荐!!!) ---------------------------------- ...
- 第四课 Makefile文件的制作(下)
1序言: 前面一节课讲解了Makefile的基础知识包括原理.预定义以及命令格式,这样是可以完成一个自动编译的文件,这些知识可以帮你完成.想想mak真是强大啊,可能有些同志发现了如果项目文件太多每个目 ...
- NoSQL2
系统的可扩展性是推动NoSQL运动发展的的主要理由,包含了分布式系统协调,故障转移,资源管理和许多其他特性.这么讲使得NoSQL听起来像是一个大筐,什么都能塞进去.尽管NoSQL运动并没有给分布式数据 ...
- Wireshark(一):Wireshark基本用法
转载:https://community.emc.com/message/818739#818739 按照国际惯例,从最基本的说起. 抓取报文: 下载和安装好Wireshark之后,启动Wiresha ...
- JavaScript的消息机制
JavaScript本身是单线程的,但它却是事件驱动的.类似Windows窗体应用程序,它也需要消息队列机制来实现.程序的执行并不是连续的,绝大多数时间都在等待消息.每次执行执行程序都是在响应消息,这 ...