WPF 依赖属性与依赖对象
在介绍依赖属性之前,我先介绍下属性的历史
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
} private void btnClick_Click(object sender, RoutedEventArgs e)
{
MyDependencyObject myDp = new MyDependencyObject();
myDp.SetValue(MyDependencyObject.FlagProperty, this.txt1.Text);
txt2.Text = (string)myDp.GetValue(MyDependencyObject.FlagProperty);
}
} public class MyDependencyObject:DependencyObject
{
public static readonly DependencyProperty FlagProperty =
DependencyProperty.Register("Flag", typeof(string), typeof(MyDependencyObject)); }
从上面的例子中,我们可以知道依赖对象作为依赖属性的宿主,才能形成完整的binding目标被数据所驱动。
其中DependencyObject是WPF相当底层的一个基类,所有的UI控件都是继承与它。它又是继承与DispatchObject.
也就是说,所有的UI控件,在WPF中,属性都是依赖属性。
另外从上面的例子中可以看出来,主要由三个部分构成:
1)注册依赖属性(还有其他重载方法):
DependencyProperty.Register(string name, Type propertyType, Type ownerType)
第一个参数是注册的属性的名称(这个名称跟将来要包装的CLR属性的名称一样),第二个参数是这个属性的返回类型,第三个是这个属性的寄托类的类型。
这里要注意,这个依赖属性的对象名称一般都比注册的名称多一个Property,这是一种潜规则,虽然也可以为其他的值。
另外注册的依赖属性对象都是public static readonly。
2)依赖对象的SetValue方法。
第一个参数是注册的依赖属性的对象名称(带Property后缀的),第二个是要设置的依赖属性的值。
3)依赖对象的GetValue方法。
参数就是注册的依赖属性的对象名称(带Property后缀的)。
说到这里,我们暂时先搁下不谈,我们先看看一个一般的UI控件的属性是什么样子的:
比如Textbox.Text属性,这个是个CLR属性,那么其跟依赖属性是什么关系了,原来在Text属性的内部,也是调用了SetValue, GetValue方法,
并且还执行了类型转换(string类型),这样就相当于用这个包装器以实例属性的形式向外界暴露依赖属性,这样一个依赖属性才能成为数据源的path,
我们再看看Textbox设定绑定的方法,其有一个SetBinding的方法,其实就是在内部调用的BindingOperations.SetBinding的方法,这也看出了微软希望能够
设置绑定的对象时UI对象。我们自己在构造依赖对象的时候,也可以构造一个SetBinding的方法,以方便调用。
如果直接使用BindingOperations:
MyDependencyObject myDpo = new MyDependencyObject();
private void DirectlyBinding()
{
BindingOperations.SetBinding(myDpo, MyDependencyObject.FlagProperty, new Binding("Text") { Source = txt1 });
BindingOperations.SetBinding(txt2, TextBox.TextProperty , new Binding("Flag") { Source = myDpo });
}
注意第一句SetBinding是把txt1的Text CLR属性绑定到myDpo这个依赖对象的FlagProperty依赖属性上。
第二句SetBinding是把myDpo的Flag CLR属性绑定到txt2这个依赖对象的TextProperty依赖属性上。
特别是第二个参数千万不要搞错了,是第一个参数中的依赖属性。
另外这个myDpo对象不能放到局部变量里面,否则是达不到效果的。
另外还有一点,如果我事先在txt2中输入了字符,在txt1中输入字符是不会更新到txt2中的,不知道为什么?
从以上我们可以看出,CLR属性其实就是依赖属性的代言人,有没有这个代言人,依赖属性都是存在的。
为什么说依赖属性没有实现INotifyPropertyChanged接口,还可以再属性的值发生改变的时候与之关联的Binding对象依然可以得到通知呢?为什么说依赖属性天生就是合格的数据源呢?
另外注册依赖属性的时候,最后还有第四个参数,这里没有列出来,其究竟能做什么呢?
依赖属性到底比起CLR属性有何优势,为什么能够节省内存开销,为什么能够通过Binding依赖在其他对象上(winform中不一样也可以吗)?
我们带着这些疑问,深入挖掘下依赖属性依赖对象的秘密。
深入挖掘:
首先从注册依赖属性看起,在注册的时候,究竟注册到哪里去了呢?
通过深入源代码,发现最核心的地方在于一个函数(DependencyProperty的静态方法):
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
FromNameKey key = new FromNameKey(name, ownerType);
lock (Synchronized)
{
if (PropertyFromName.Contains(key))
{
throw new ArgumentException(SR.Get(SRID.PropertyAlreadyRegistered, name, ownerType.Name));
}
}
....
// Create property
DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
// Build key
lock (Synchronized)
{
PropertyFromName[key] = dp;
}
......
return dp;
}
其中: private static Hashtable PropertyFromName = new Hashtable();
当我们注册一个依赖属性的时候,通过FromNamekey来生成一个hashcode(通过注册的名称异或宿主得到),构造一个依赖属性,并且存到PropertyFroamName这个
哈希表里面,这其中还检查依赖属性是否独一无二。
在依赖属性的构造函数里面:
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
this._name = name;
this._propertyType = propertyType;
this._ownerType = ownerType;
this._defaultMetadata = defaultMetadata;
this._validateValueCallback = validateValueCallback;
DependencyProperty.Flags flags;
lock (DependencyProperty.Synchronized)
{
flags = (DependencyProperty.Flags)DependencyProperty.GetUniqueGlobalIndex(ownerType, name);
DependencyProperty.RegisteredPropertyList.Add(this);
}
if (propertyType.IsValueType)
{
flags |= DependencyProperty.Flags.IsValueType;
}
if (propertyType == typeof(object))
{
flags |= DependencyProperty.Flags.IsObjectType;
}
if (typeof(Freezable).IsAssignableFrom(propertyType))
{
flags |= DependencyProperty.Flags.IsFreezableType;
}
if (propertyType == typeof(string))
{
flags |= DependencyProperty.Flags.IsStringType;
}
this._packedData = flags;
}
其中GetUniqueGlobalIndex()方法,使得我们得到了依赖属性的唯一索引号,然后把依赖属性加到RegisteredPropertyList列表里面,它是一个静态成员;
internal static ItemStructList<DependencyProperty> RegisteredPropertyList = new ItemStructList<DependencyProperty>(768);
注册完成后,一个依赖属性实例就注册到了一个全局的Hashtable中去了(通过名称和宿主保证唯一性),而每个依赖属性又有唯一的索引号去表示,那么接下来就
是如何使用依赖对象的SetValue和GetValue借助这个依赖属性实例保存及读取值了。
这里自然就要去跟踪依赖对象的GetValue方法了:
public object GetValue(DependencyProperty dp)
{
base.VerifyAccess();
if (dp == null)
{
throw new ArgumentNullException("dp");
}
return this.GetValueEntry(this.LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value;
}
通过依赖属性的实例我们就可以得到这个实例对应的值,关键在于最后return的那句话,其中用到了dp.GlobalIndex.
其中DependencyObject有一个数组EffectiveValueEntry[] _effectiveValueEntry,这个变量里面保存了值和索引(就是GlobalIndex),通过dp.GlobalIndex我们就可以取得相应的EffectiveValueEntry,其Value就是我们要找的值。如果数组没有包含这个值,就会返回依赖属性的默认值,是由DefaultMetadata提供。
SetValue方法的奥秘,跟GetValue类似,我们肯定也是把值存进EffectiveValueEntry数组里面。
说道这里,我举一个例子来说明,假设有一个依赖对象类A申明了10个依赖对象,A中注册了5个依赖属性,另外一个依赖对象类B申明了8个依赖对象,B中注册了4个依赖属性。
那么在AB都注册完后,DependencyProperty的静态成员PropertyFromName和RegisterPropertyList就有5+4=9个成员。
在A申明的10个依赖对象中,每个依赖对象都会有EffectiveValueEntry这个集合,那么也就是说每个对象都有存取5个值的能力,也就是说每个对象都可以开5个房间存取值。
B申明的8个依赖对象也是一样,每个对象都有存取4个值的能力,存取值的索引每个都是一样的,也就是说B1对象和B2对象针对同样的依赖属性其索引是一样的。
假设A的第一个对象A1设置了3个依赖属性的值,那么这个对象A1的EffectiveValueEntry只有3个成员,其他没有设置依赖属性的值的对象,他们的EffectiveValueEntry是没有成员的。
在以前如果使用属性,那么总共需要内存开销(假设每个字段消耗1个字节),10*5+8*4 = 82个字节,那么现在因为只设置了一个对象的3个属性值,那么其实就只消耗了3个字节,所以这也就说明了为什么依赖属性可以节省空间的原因了,以时间来换取空间。
总结:
到目前为止,我们知道了依赖对象与依赖属性是息息相关的,也知道了为什么能够节省空间的原因,所有的UI控件的属性都是依赖属性,所以说,如果我们自己要写控件,依赖属性也是必不可少要写的方面,知道了依赖属性的深层次原理,下次写起来就不会那么费劲了,另外注册的时候最后还有一个参数这里没有解释,相信也不是很难,这里就略过了。例子就不用附带了。
WPF 依赖属性与依赖对象的更多相关文章
- WPF入门教程系列十一——依赖属性(一)
一.依赖属性基本介绍 本篇开始学习WPF的另一个重要内容依赖属性. 大家都知道WPF带来了很多新的特性,其中一个就是引入了一种新的属性机制——依赖属性.依赖属性出现的目的是用来实现WPF中的样式.自动 ...
- WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
一. 摘要 首先圣殿骑士非常高兴这个系列能得到大家的关注和支持.这个系列从七月份開始到如今才第七篇,上一篇公布是在8月2日,掐指一算有二十多天没有继续更新了,最主要原因一来是想把它写好,二来是由于近期 ...
- WPF学习(5)依赖属性
今天我们来学习WPF一个比较重要的概念:依赖属性.这里推荐大家看看周永恒大哥的文章,讲的确实很不错.我理解的没那么深入,只能发表一下自己的浅见.提到依赖属性,不得不说我们经常使用的传统的.net属性, ...
- WPF 依赖属性和附加属性
依赖属性: 依赖属性就是自己没有值,通过Binding从数据源获得值,就是依赖在别人身上,拥有依赖属性的对象称为依赖对象. 依赖属性的值存在哪里? 在WPF运行时,维护了一个全局的Hashtable存 ...
- WPF系列 —— 控件添加依赖属性(转)
WPF系列 —— 控件添加依赖属性 依赖属性的概念,用途 ,如何新建与使用.本文用做一个自定义TimePicker控件来演示WPF的依赖属性的简单应用. 先上TimePicker的一个效果图. 概念 ...
- WPF系列 —— 控件添加依赖属性
依赖属性的概念,用途 ,如何新建与使用.本文用做一个自定义TimePicker控件来演示WPF的依赖属性的简单应用. 先上TimePicker的一个效果图. 概念 和 用途:依赖属性是对传统.net ...
- WPF 之 依赖属性与附加属性(五)
一.CLR 属性 程序的本质是"数据+算法",或者说用算法来处理数据以期得到输出结果.在程序中,数据表现为各种各样的变量,算法则表现为各种各样的函数(操作符是函数的简记法). ...
- 《WPF程序设计指南》读书笔记——第8章 依赖属性
1.依赖属性的效果 一旦规定视觉树上一个对象的fontsize属性,那么属于他的节点之下的所有对象都会沿袭这个属性,然而如果某个子节点明确的设定了自己的fontsize,就不会沿袭父节点的fontsi ...
- WPF之依赖属性和附加属性
参考资料: 一站式WPF--依赖属性(DependencyProperty)一 一站式WPF--依赖属性(DependencyProperty)二 依赖属性之我见: 这两篇文章介绍的 ...
随机推荐
- 见过NTP服务,没见过网络流量到200M左右的NTP服务
XXX,看来可能是NTP.CONF的文件配置错误所致了. 附上一段查看网络流量的SHELL.(好像只针对ETH0,如果要看其它的,还需要修改) #!/bin/bash typeset in_old d ...
- KeilC51使用详解 (一)
第一节 系统概述 Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上.结构性.可读性.可维护性上有明显的优势,因而易学易用.用过 ...
- LA 4726 再看斜率优化
感觉最近一批解题报告没写,现在慢慢补吧,算是noip前攒攒rp了 首先感到深深的自责,因为之前对斜率优化没有深入的理解,只是记住了一般步骤,并没有完全了解为什么这样做 先就这道题目而言 首先这种序列题 ...
- CH Round #48 - Streaming #3 (NOIP模拟赛Day1)
A.数三角形 题目:http://www.contesthunter.org/contest/CH%20Round%20%2348%20-%20Streaming%20%233%20(NOIP模拟赛D ...
- (转载)mysql 用drop和delete方法删除用户的区别
(转载)http://hi.baidu.com/yymagento/item/56c3f6184bce8347e75e06db 在学习drop方法删除用户时,按照书上讲的一直没操作成功,后来到网上查了 ...
- 树状数组(二维):COGS 1532 [IOI2001]移动电话
1532. [IOI2001]移动电话 ★☆ 输入文件:mobilephones.in 输出文件:mobilephones.out 简单对比 时间限制:5 s 内存限制:256 MB ...
- Uncle Sam 山姆大叔
山姆大叔被用来代指“美国”或“美国政府”,主要在美国.英国,尤其是在新闻界中使用较多.“山姆大叔”是美国的绰号,它同自由女神一样,为世人所熟知. 形象 美国的报纸杂志.文学作品和漫画中,经常可以看到“ ...
- 《A First Course in Probability》-chape6-随机变量的联合分布-基本概念
之前我们探讨了一元随机变量的分布列,下面我们应该将相应的性质推广到多元随机变量的分布列,在这里我们主要以讨论二元随机变量分布列为主. 利用类比的方法,我们很容易将一元随机变量的分布列的性质推广上来. ...
- #345 div2 D. Image Preview
Vasya's telephone contains n photos. Photo number 1 is currently opened on the phone. It is allowed ...
- poj 3304 找一条直线穿过所有线段
题目链接:http://poj.org/problem?id=3304 #include<cstdio> #include<cstring> #include<cmath ...