您的位置:首页 > 职场人生

WPF基础到企业应用系列7——深入剖析依赖属性(六)

2010-08-27 09:46 771 查看

十五. 模拟依赖属性实现

  古人有”不入虎穴焉得虎子“的名句,我们今天也试着入一入虎穴,探探依赖属性里面到底藏着什么不可告人的秘密,在往下讲之前,我们先来看一下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, DependencyProperty>();
//存储元数据的集合
private List<PropertyMetadata> _metadataMap = new List<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, Type propertyType, Type ownerType, 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, Type propertyType, Type ownerType)
{
return Register(name, propertyType, ownerType, new PropertyMetadata());
}

//注册的公用方法,把这个依赖属性加入到IDictionary的键值集合中,Key为name和owner_type的GetHashCode取异,Value就是我们注册的DependencyProperty
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
DependencyProperty property = new DependencyProperty(name, propertyType, ownerType, 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, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
{
DependencyProperty property = Register(name, propertyType, ownerType, typeMetadata);
return property;
}

//注册附加依赖属性
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType)
{
return RegisterAttached(name, propertyType, ownerType, new PropertyMetadata(), null);
}

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
return RegisterAttached(name, propertyType, ownerType, defaultMetadata, null);
}

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, Type validatorType)
{
DependencyProperty property = Register(name, propertyType, ownerType, defaultMetadata);
property.attached = true;
property.validator_type = validatorType;
return property;
}

//子类继承重写以及其他需要重写Metadata的时候使用
public void OverrideMetadata(Type forType, 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 List<EffectiveValueEntry> _effectiveValues = new List<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, DependencyPropertyOptions options)
{
default_value = defaultValue;
this.options = options;
}

public PropertyMetadata(DependencyPropertyOptions options, params Attribute[] attributes)
{
this.options = options;
this.attributes = attributes;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options, params Attribute[] attributes)
{
this.options = options;
default_value = defaultValue;
this.attributes = attributes;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options, GetValueOverride getValueOverride, SetValueOverride setValueOverride)
{
this.options = options;
default_value = defaultValue;
set_value = setValueOverride;
get_value = getValueOverride;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options, GetValueOverride getValueOverride, SetValueOverride setValueOverride, params 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, DependencyProperty dp)
{
// 实现元数据继承之间的合并
}

protected virtual void OnApply(DependencyProperty dependencyProperty, Type targetType)
{
// 当元数据被这个属性应用,OnApply就会被触发,在此时元数据也将被密封起来。
}
}
}


前面我们实现了一个简单的依赖属性系统,现在就得先测试一下其功能,代码如下:

class Program : DependencyObject
{
public static readonly DependencyProperty CounterProperty;
static Program()
{
//注册依赖属性Counter
CounterProperty = DependencyProperty.Register("Counter",
typeof(double),
typeof(Program),
new 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;
Console.WriteLine("通过SetValue设置改变了的值: " + pro2.Counter.ToString());
Console.ReadLine();
}
}

那么测试结果为:





利用VS自带的类图,可以看到刚才我们实现的这个依赖属性类及类之间的关系图:



由于上面的代码在很多方面都很粗糙,所以希望大家能下载代码进行改造,同时也希望给出反馈。

十六. 本文总结

  这篇文章洋洋洒洒写了很多,我们现在简单回顾一下:在开篇之前我们会先介绍比本篇更重要的一些东西,然后插播了一段”云计算之旅“的广告(广告费很昂贵

,所以格外小心),作为最近几个月执着研究的东西,终于可以在下周和大家见面了,所以心中甚是喜悦。在前面的两个内容之后我们正式进入本篇的主题——依赖 属性。依赖属性是WPF的核心概念,所以我们花费了大量的时间和篇幅进行论述,首先从依赖属性基本介绍讲起,然后过渡到依赖属性的优先级、附加属性、只读 依赖属性、依赖属性元数据、依赖属性回调、验证及强制值、依赖属性监听、代码段(自动生成) 等相关知识,最后我们模拟了一个WPF依赖属性的实现,对内部实现原理进行了一些研究。在接下来的三篇”剖析路由事件”、”剖析命令”、”剖析绑定”也会 采用这篇文章的风格,希望能尽量说透,如果有误之处还希望各位能够批评指正!

十七. 相关代码下载

  在文章的最后,我们提供代码的下载,这几篇文章最重要的就是下载代码来细细研究,代码里面也添加了比较详细的注释,如果大家有什么问题,也可以和我联系,如果有不正确的地方也希望多多海涵并能给我及时反馈,我将感激不尽!





上图就是整个代码包的结构图,下载链接:DependencyPropertiesDemo.rar

十八.系列进度

· 1. WPF 基础到企业应用系列1——开篇有益
· 2. WPF 基础到企业应用系列2——WPF前世今生
· 3. WPF 基础到企业应用系列3——WPF开发漫谈
· 4. WPF 基础到企业应用系列4——WPF千年轮回
· 5. 使用面板做布局(几种布局控件的XAML及CS代码,综合布局等)
· 6. 依赖属性、附加属性(基本、继承、元数据)
· 7. 路由事件、附加事件
· 8. 命令
· 9. WPF控件分类介绍与使用技巧(ContentControl、HeaderedContentControl…… Decorator)
· 10. 尺寸缩放、定位与变换元素
· 11. 资源
· 12. 数据绑定(基本、值转换、验证、集合的筛选、排序、分组、主从、数据提供者)
· 13. 样式
· 14. 模板
· 15. 多语言、皮肤和主题
· 16. 2D图形
· 17. 3D图形
· 18. 动画(几种动画的应用)
· 19. 音频、视频、语音
· 20. 文档、打印、报表
· 21. 用户控件和自定义控件
· 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新功能
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息