写在之前:

依赖属性算是WPF醉醉基础的一个组成了。平时写代码的时候,简单的绑定很轻松,但是遇到复杂的层次比较多的绑定,真的是要命。所以,我觉得深刻认识依赖属性是很有必要的。本篇只是个人学习的记录,学习的博客是周永恒先生的《一站式WPF--依赖属性(DependencyProperty)》,这算是一个系列了,说的很详细。如果需要更好的学习,建议移步上述原文,受益匪浅。

什么是依赖属性?

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

也就是说,WPF提供一组叫做‘WPF属性系统’的服务,而依赖属性就是被这个服务所支持的属性。我只能说,每个字都认识,但是放在一起认识的就不那么清晰了……

首先,想要了解它,必须知道它是为了什么而来。

虽然不清楚依赖属性,但是属性我们是很清楚的,封装类的字段,表示类的状态,编译后被转化为get_,set_方法,可以被类或结构等使用,常见的一个属性如下:

    public class ClassObject
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}

既然已经有了属性,为什么还要有依赖属性呢?必然是属性有一些缺点了,而依赖属性恰好能够解决这个问题。以Button为例:

每次继承,父类的私有字段都被继承下来。对Button来说,大多数属性并没有被修改,仍然保持父类定义时的默认值。通常情况下,在整个Button的对象生命周期中,也只有少部分属性被修改。也已看得出来:

  1. 每次继承,子类对象都会获得更多的属性,这样,继承树的低端对象不可避免的膨胀;
  2. 既然大多数属性没有被修改,那么就可以把这些属性从对象中剥离,减少对象的体积;

可以知道,依赖属性就是为了解决这个问题诞生了。首先定义依赖属性,它里面存储之前2中希望剥离的属性:

    public class DependencyProperty
{
// 维护了一个全局的Map用来储存所有的DP
internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
internal string Name;//注册属性名称
internal object Value;//属性值
internal object HashCode;//Mape唯一键值 private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
{
this.Name = name;
this.Value = defaultValue;
this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
}
// 对外暴露一个Register方法用来注册新的DP
public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue)
{
DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue);
RegisteredDps.Add(dp.HashCode, dp);
return dp;
}
}

然后定义DependencyObject来使用DP:

    public class DependencyObject
{
// 注册一个新的DP:NameProperty
public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty); public object GetValue(DependencyProperty dp)
{
return DependencyProperty.RegisteredDps[dp.HashCode].Value;
} public void SetValue(DependencyProperty dp, object value)
{
DependencyProperty.RegisteredDps[dp.HashCode].Value = value;
} public string Name
{
get
{
return (string)GetValue(NameProperty);
}
set
{
SetValue(NameProperty, value);
}
}
}

DependencyObject和文章开篇的ClassObject中的Name有什么不同呢?

>>DependencyObject.Name的实际值不是用字段保存在DependencyObject中,而是保存在NameProperty中,通过SetValue和GetValue来金星赋值取值操作。

在上述例子中,所有DependncuObject的对象将共用一个NameProperty,这在现实中是不实际的:只要修改一个对象的属性,相当于所有对象的属性值都被修改了。所以,修改的属性,还是要维护在相应的对象中的:(修改部分用☆表示)

    public class DependencyProperty
{
private static int globalIndex = ;// ☆
// 维护了一个全局的Map用来储存所有的DP
internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
internal string Name;//注册属性名称
internal object Value;//属性值
internal int Index;// ☆
internal object HashCode;//Mape唯一键值 private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
{
this.Name = name;
this.Value = defaultValue;
this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
}
// 对外暴露一个Register方法用来注册新的DP
public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue)
{
DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue);
globalIndex++;// ☆
dp.Index = globalIndex; // ☆
RegisteredDps.Add(dp.HashCode, dp);
return dp;
}
}  

所有修改过的DP都保存在EffectiveValueEntry里,这样,就可以只保存修改的属性,未修改过的属性仍然读取DP的默认值,优化了属性的储存。

    public class DependencyObject
{
// 引入有效的概念// ☆
private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();
// 注册一个新的DP:NameProperty
public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty); public object GetValue(DependencyProperty dp)
{ // ☆
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index);
if (effectiveValue.PropertyIndex != 0)
{
return effectiveValue.Value;
}
else
{
return DependencyProperty.RegisteredDps[dp.HashCode].Value;//仅此部分相同
}
} public void SetValue(DependencyProperty dp, object value)
{ // 全部 ☆
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
};
}
//DependencyProperty.RegisteredDps[dp.HashCode].Value = value;
} public string Name
{
get
{
return (string)GetValue(NameProperty);
}
set
{
SetValue(NameProperty, value);
}
}
}
    internal struct EffectiveValueEntry
{
internal int PropertyIndex{get;set;}
internal object Value{get;set;}
}

WPF依赖属性DependencyProperty的更多相关文章

  1. WPF 依赖属性源码 洞察微软如何实现DependencyProperty

    依赖属性DependencyProperty是wpf最重要的一个类,理解该类如何实现对学习wpf帮助很大! 终于找到了该类的源码!仔细阅读源码,看看微软如何玩的花招! File: Base\Syste ...

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

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

  3. WPF依赖属性详解

    WPF依赖属性详解 WPF 依赖属性 英文译为 Dependency Properties,是WPF引入的一种新类型的属性,在WPF中有着极为广泛的应用,在WPF中对于WPF Dependency P ...

  4. WPF自学入门(五)WPF依赖属性

    在.NET中有事件也有属性,WPF中加入了路由事件,也加入了依赖属性.最近在写项目时还不知道WPF依赖属性是干什么用的,在使用依赖项属性的时候我都以为是在用.NET中的属性,但是确实上不是的,通过阅读 ...

  5. WPF依赖属性值源(BaseValueSource)

    原文:WPF依赖属性值源(BaseValueSource)   WPF依赖属性提供一个机制,可以获取依赖属性提供值的来源 其以BaseValueSource枚举表示 1.Default public ...

  6. WPF依赖属性(续)(3)依赖属性存储

    原文:WPF依赖属性(续)(3)依赖属性存储          在之前的两篇,很多朋友参与了讨论,也说明各位对WPF/SL计数的热情,对DP系统各抒已见,当然也出现了一些分歧. 以下简称DP为依赖属性 ...

  7. WPF依赖属性(续)(2)依赖属性与附加属性的区别

    原文:WPF依赖属性(续)(2)依赖属性与附加属性的区别        接上篇,感谢各位的评论,都是认为依赖属性的设计并不是为了节省内存,从大的方面而讲是如此.样式,数据绑定,动画样样都离不开它.这篇 ...

  8. WPF依赖属性的正确学习方法

    前言 我在学习WPF的早期,对依赖属性理解一直都非常的不到位,其恶果就是,我每次在写依赖属性的时候,需要翻过去的代码来复制黏贴. 相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学 ...

  9. WPF依赖属性(续)(1)

    原文:WPF依赖属性(续)(1)                 之前有写过几篇文章,详细地介绍了依赖属性的基本使用方法,如果你不想了解其内部实现机制的话,那么通过那两篇文章的介绍,足以应付平时的应用 ...

随机推荐

  1. cocos2d-x quick 学习 一 环境

    最近几天都在学习quick 一直也在查找资料. 本来这篇文章在昨晚就能写好的.可是昨晚环境遇到点问题自己没想通. 正题:首先是环境配置: 由于我在mac下 所以在网上找了很多资料提前看了.  我之前也 ...

  2. Hi, there!

    Wish you a happy day~我们一起玩WEB前端!^ ^

  3. Linq解析带命名空间、前缀、Soap格式的XML

    关于XML,经常会用到,XML有一般的,同样也有二般的,更不要觉得会操作基础的XML就觉得自己已经精通XML操作,文中是对解析XML的方法进行介绍 1. 一般XML <?xml version= ...

  4. PLSQL Developer导入Excel数据

    LSQL Developer导入Excel数据 最近处理将Excel数据导入Oracle的工作比较多.之前都是采用Sqlldr命令行导入的方式处理.每次导入不同格式的Excel表数据,都需要先把Exc ...

  5. AB窗体互传参数

    一.找了好几个,都不靠谱,不是说不靠谱,自己感觉太繁琐 二.父窗口传子窗口好传,有两种方法(自己认为比较简单的方法哈), 1第一种方法:在子窗口中新建一个属性:再新建一个方法,当然方法就是把属性个窗体 ...

  6. sql存储过程比sql语句执行慢很多

    参数嗅探的问题 原因:(1)可能是发生了参数嗅探,第一次赋给存储过程的输入参数,会为该存储过程生成一个基于输入参数的执行计划,因此如果第一次输入的参数不具有代表性(例如大部分查询输入的参数都是A值,但 ...

  7. 一行R代码来实现繁琐的可视化

    ggfortify 有着简单易用的统一的界面来用一行代码来对许多受欢迎的R软件包结果进行二维可视化的一个R工具包.这让许多的统计学家以及数据科学家省去了许多繁琐和重复的过程,不用对结果进行任何处理就能 ...

  8. JavaScript的chapterI

    JavaScript是一种由web浏览器进行解析和执行脚本语言,包括ECMAScript,DOM,BOM. JavaScript的最大作用是赋予页面动作,让页面动起来,已经动态的处理页面的数据. 应用 ...

  9. 修改shell提示符的显示格式

    默认的提示符显示方式如下: [root@localhost foldername]# 由于太长,希望能缩短到: [foldername]# 修改方式: # vim /etc/bashrc 找到第41行 ...

  10. for循环j = j++ 和 j = ++j

    package com.test.forname; public class TestForName { public static void main(String[] args) throws E ...