十五. 模拟依赖属性实现
古人有”不入虎穴焉得虎子“的名句,我们今天也试着入一入虎穴,探探依赖属性里面到底藏着什么不可告人的秘密,在往下讲之前,我们先来看一下DependencyObject 、DependencyProperty 以及PropertyMetadata到底包含哪些功能,如下面三幅图
通过前面三幅图,我们就可以了解WPF依赖属性系统的大体结构以及主要功能,再者通过前面我们对它的使用,对它的内部实现也有一个相对比较清晰 的认识,那么接下来要做的就是:借助Reflector+VS调试内部代码功能一起来研究其内部的实现原理。 本来想详细写清楚开发的过程,但是有点多,所以我打算直接讲这几个类。大家也可以通过这个思路来试一试,同时还可以参考Mono的源码、WF的依赖属性源 码等。这里要推荐的是周永恒的博客,此人对技术的理解很是透彻,博文虽少,但每篇都堪称经典,所以他的文章,我都通读三遍。虽然大多概念都懂,并且读到深 处也能产生共鸣,其最主要目的还是学习他这种”阐述问题的思路“,后来也和此人MSN聊过几次。所以这个依赖属性的框架在某些程度上也借鉴了他的一些写 法。
有了前面的思路,首先定义DependencyProperty这个类,它里面存储前面我们提到希望抽出来的字段。DependencyProperty内部维护了一个全局的Map用来储存所有的DependencyProperty,对外暴露了一个Register方法用来注册新的DependencyProperty。当然,为了保证在Map中键值唯一,注册时需要根据传入的名字和注册类的的 HashCode取异或来生成Key。 所以我们就可以完成DependencyProperty类了,代码如下,介绍详见代码注释。:
public sealed class DependencyProperty
{ //全局的IDictionary用来储存所有的DependencyProperty
internal static IDictionary<int,DependencyProperty> properties = new Dictionary<int,175);">DependencyProperty>(); //存储元数据的集合
private List<PropertyMetadata> _MetadataMap = new PropertyMetadata>(); private static int globalIndex = 0; private PropertyMetadata def_Metadata; private bool attached; private string name; private int _index; private Type owner_type; private Type property_type; private Type validator_type; // 构造函数
private DependencyProperty() { } //构造函数私有,保证外界不会对它进行实例化
private DependencyProperty(string name,175);">Type propertyType,175);">Type ownerType,175);">PropertyMetadata defaultMetadata) { this.name = name; property_type = propertyType; owner_type = ownerType; def_Metadata = defaultMetadata; } // 常用属性
public PropertyMetadata DefaultMetadata { get { return def_Metadata; } } public bool IsAttached { get { return attached; } } public int Index { get { return _index; } set { _index = value; } } public string Name { get { return name; } } public Type OwnerType { get { return owner_type; } } public Type PropertyType { get { return property_type; } } public Type ValidatorType { get { return validator_type; } } public override int GetHashCode() { return name.GetHashCode() ^ owner_type.GetHashCode(); } //注册依赖属性
public static DependencyProperty Register(string name,175);">Type ownerType) { return Register(name,propertyType,ownerType,new PropertyMetadata()); } //注册的公用方法,把这个依赖属性加入到IDictionary的键值集合中,Key为name和owner_type的GetHashCode取异,Value就是我们注册的DependencyProperty
public static PropertyMetadata defaultMetadata) { DependencyProperty property = new DependencyProperty(name,defaultMetadata); globalIndex++; property.Index = globalIndex; if (properties.ContainsKey(property.GetHashCode())) { throw new InvalidOperationException("A property with the same name already exists"); } //把刚实例化的DependencyProperty添加到这个全局的IDictionary种
properties.Add(property.GetHashCode(),property); return property; } //注册只读依赖属性
public static DependencyProperty RegisterReadOnly(string name,175);">PropertyMetadata typeMetadata) { DependencyProperty property = Register(name,typeMetadata); return property; } //注册附加依赖属性
public static DependencyProperty RegisterAttached(string name,175);">Type ownerType) { return RegisterAttached(name,175);">PropertyMetadata(),null); } public static PropertyMetadata defaultMetadata) { return RegisterAttached(name,defaultMetadata,175);">PropertyMetadata defaultMetadata,175);">Type validatorType) { Metadata); property.attached = true; property.validator_type = validatorType; return property; } //子类继承重写以及其他需要重写Metadata的时候使用
public void OverrideMetadata(Type forType,175);">PropertyMetadata Metadata) { Metadata.Type = forType; _MetadataMap.Add(Metadata); } //获取元数据信息
public PropertyMetadata GetMetadata(Type type) { PropertyMetadata medatata = _MetadataMap.FirstOrDefault((i) => i.Type == type) ?? _MetadataMap.FirstOrDefault((i) => type.IsSubclassOf(i.Type)); if (medatata == null) { medatata = def_Metadata; } return medatata; } }
有了DependencyProperty ,那么接下来就需要定义DependencyObject 来使用这个DependencyProperty 。首先使用DependencyProperty .Register方法注册了一个新的DependencyProperty ,然后提供了GetValue和SetValue两个方法来操作刚刚构造的DependencyProperty 。这个时候我们看到一个简单的依赖属性系统已初见端倪了,详见代码注释。
namespace Realize_DPs { public abstract class DependencyObject : IDisposable
{ //添加一个List来记录修改信息
private EffectiveValueEntry> _effectiveValues = new EffectiveValueEntry>(); //属性包装器,通过它来访问依赖属性
public object GetValue(DependencyProperty dp) { //首先通过判断是否改动过,以此来决定是读元数据的默认值还是改动了的值
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index); if (effectiveValue.PropertyIndex != 0) { return effectiveValue.Value; } else
{ PropertyMetadata Metadata; Metadata = DependencyProperty.properties[dp.GetHashCode()].DefaultMetadata; return Metadata.DefaultValue; } } //属性包装器,通过它来设置依赖属性的值
public void SetValue(DependencyProperty dp,object value) { //首先通过判断是否改动过,以及改动过,则继续对改动过的元素赋值,否则对_effectiveValues增加元素
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index); if (effectiveValue.PropertyIndex != 0) { effectiveValue.Value = value; } else
{ effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index,Value = value }; _effectiveValues.Add(effectiveValue); } } public void Dispose() { //暂时还没有处理
} } internal struct EffectiveValueEntry
{ internal int PropertyIndex { get; set; } internal object Value { get; set; } } }
前面有了DependencyProperty 和DependencyObject 类,那我们现在来新建一个比较重要的类 PropertyMetadata ,它的作用和功能很强大,我们这里只是简单进行了构建,如下代码:
namespace Realize_DPs { public delegate void SetValueOverride(DependencyObject d,object value); public delegate object GetValueOverride(DependencyObject d); public class PropertyMetadata
{ private object default_value; private DependencyPropertyOptions options = DependencyPropertyOptions.Default; private bool _sealed = false; private SetValueOverride set_value; private GetValueOverride get_value; private Attribute[] attributes; private Type type; // 构造函数重载
public PropertyMetadata() { } public PropertyMetadata(object defaultValue) { default_value = defaultValue; } public PropertyMetadata(DependencyPropertyOptions options) { this.options = options; } public PropertyMetadata(params Attribute[] attributes) { this.attributes = attributes; } public PropertyMetadata(object defaultValue,params Attribute[] attributes) { default_value = defaultValue; this.attributes = attributes; } public PropertyMetadata(object defaultValue,175);">DependencyPropertyOptions options) { default_value = defaultValue; this.options = options; } public PropertyMetadata(DependencyPropertyOptions options,175);">Attribute[] attributes) { this.options = options; this.attributes = attributes; } public PropertyMetadata(object defaultValue,175);">Attribute[] attributes) { this.options = options; default_value = defaultValue; this.attributes = attributes; } public PropertyMetadata(object defaultValue,175);">GetValueOverride getValueOverride,175);">SetValueOverride setValueOverride) { this.options = options; default_value = defaultValue; set_value = setValueOverride; get_value = getValueOverride; } public PropertyMetadata(object defaultValue,175);">SetValueOverride setValueOverride,175);">Attribute[] attributes) { this.options = options; default_value = defaultValue; set_value = setValueOverride; get_value = getValueOverride; this.attributes = attributes; } // 常用属性
public object DefaultValue { get { return default_value; } set { default_value = value; } } public GetValueOverride GetValueOverride { get { return get_value; } set { get_value = value; } } public bool IsMetaProperty { get { return (options & DependencyPropertyOptions.Metadata) == DependencyPropertyOptions.Metadata; } } public bool IsNonSerialized { get { return (options & DependencyPropertyOptions.NonSerialized) == DependencyPropertyOptions.NonSerialized; } } public bool IsReadOnly { get { return (options & DependencyPropertyOptions.Readonly) == DependencyPropertyOptions.Readonly; } } protected bool IsSealed { get { return _sealed; } } public DependencyPropertyOptions Options { get { return options; } set { options = value; } } public SetValueOverride SetValueOverride { get { return set_value; } set { set_value = value; } } public Type Type { get { return type; } set { type = value; } } protected virtual void Merge(PropertyMetadata baseMetadata,175);">DependencyProperty dp) { // 实现元数据继承之间的合并
} protected virtual void OnApply(DependencyProperty dependencyProperty,175);">Type targetType) { // 当元数据被这个属性应用,OnApply就会被触发,在此时元数据也将被密封起来。
} } }
前面我们实现了一个简单的依赖属性系统,现在就得先测试一下其功能,代码如下:
class Program : DependencyObject
{ public static readonly DependencyProperty CounterProperty; static Program() { //注册依赖属性Counter
CounterProperty = DependencyProperty.Register("Counter",typeof(double),typeof(Program),175);">PropertyMetadata(8.0)); } //属性包装器,暴露读写接口
public double Counter { get { return (double)GetValue(CounterProperty); } set {SetValue(CounterProperty,value); } } static void Main(string[] args) { Program pro = new Program(); Console.WriteLine("读取元数据设置的默认值: "+pro.Counter.ToString()); Program pro2 = new Program(); pro2.Counter = 22.5; "通过SetValue设置改变了的值: " + pro2.Counter.ToString()); Console.ReadLine(); } }
那么测试结果为:
利用VS自带的类图,可以看到刚才我们实现的这个依赖属性类及类之间的关系图:
由于上面的代码在很多方面都很粗糙,所以希望大家能下载代码进行改造,同时也希望给出反馈。
十六. 本文总结
这篇文章洋洋洒洒写了很多,我们现在简单回顾一下:在开篇之前我们会先介绍比本篇更重要的一些东西,然后插播了一段”云计算之旅“的广告(广告费很昂贵
PHP?refimg=" + this.src)" alt="" src="http://img.jb51.cc/vcimg/static/loading.png" src="http://www.zu14.cn/coolemotion/emotions/zz_1.gif">
,所以格外小心),作为最近几个月执着研究的东西,终于可以在下周和大家见面了,所以心中甚是喜悦。在前面的两个内容之后我们正式进入本篇的主题——依赖 属性。依赖属性是WPF的核心概念,所以我们花费了大量的时间和篇幅进行论述,首先从依赖属性基本介绍讲起,然后过渡到依赖属性的优先级、附加属性、只读 依赖属性、依赖属性元数据、依赖属性回调、验证及强制值、依赖属性监听、代码段(自动生成) 等相关知识,最后我们模拟了一个WPF依赖属性的实现,对内部实现原理进行了一些研究。在接下来的三篇”剖析路由事件”、”剖析命令”、”剖析绑定”也会 采用这篇文章的风格,希望能尽量说透,如果有误之处还希望各位能够批评指正!十七. 相关代码下载
在文章的最后,我们提供代码的下载,这几篇文章最重要的就是下载代码来细细研究,代码里面也添加了比较详细的注释,如果大家有什么问题,也可以和我联系,如果有不正确的地方也希望多多海涵并能给我及时反馈,我将感激不尽!
上图就是整个代码包的结构图,下载链接:DependencyPropertiesDemo.rar
十八.系列进度
原文链接:https://www.f2er.com/javaschema/287477.html· 8. 命令· 9. WPF控件分类介绍与使用技巧(ContentControl、HeaderedContentControl…… Decorator)· 10. 尺寸缩放、定位与变换元素· 11. 资源· 12. 数据绑定(基本、值转换、验证、集合的筛选、排序、分组、主从、数据提供者)· 13. 样式· 14. 模板· 15. 多语言、皮肤和主题· 16. 2D图形· 17. 3D图形· 18. 动画(几种动画的应用)· 19. 音频、视频、语音· 20. 文档、打印、报表· 22. Win32、Windows Form以及ActiveX之间的互用性· 23. 构建并部署应用程序(ClickOnce部署、微软setup /InstallShield+自动更新组件)· 24. WPF的模式讲解及实例(MVC Demo)· 25. WPF的模式讲解及实例(MVP Demo)· 26. WPF的模式讲解及实例(MVVM Demo)· 27. 性能优化(WPF项目的瓶颈)· 28.一个完整WPF项目(普通架构版)· 39. 一个完整WPF项目(MVVM架构版)· 30. WPF 4.0新功能