依赖属性是WPF中的概念,很多WPF书籍用专门的一章来讲解,初学者经常迷惑,我多次给同事网友解释过。
本文的目的就是想把简单的事情讲清楚,如果你是初学者,欢迎光临。如果你早已熟悉,谢谢光临。

(一)首先我们来看看普通的属性和依赖属性的定义方式: 

【普遍属性】

public class MyTextBlock
{
    
private double _fontSize = 12;
    
public double FontSize { get { return _fontSize; } set{ _fontSize = value; } }

 

【依赖属性】 

public class MyTextBlock : DependencyObject 

{
    public static readonly DependencyProperty FontSizeProperty =
        DependencyProperty.Register("FontSize"typeof(double), typeof(MyTextBlock), 12.0/*缺省值*/);

    
public double FontSize
    {
        
get { return (double)base.GetValue(FontSizeProperty); }
        
set { base.SetValue(FontSizeProperty, value); }
    }
}

第一点:要定义一个static readonly的类型为DependencyPropertyFontSizeProperty字段;

第二点:MyTextBlock要直接或间接继承DependencyObject
第三点:在FontSize属性的get中调用基类(DependencyObject)的GetValue,在set中调用基类的SetValue;
其中第一点,FontSizePropertystatic。这是初学者最容易迷惑的。为什么是static呢?不管有多少MyTextBlock对象,它们的缺省FontSize都是12,属性名都是“FontSize”
所以FontSizeProperty是static 。
(二) 依赖属性有什么好处呢?
首先,节省空间:在一个程序的窗口中会有很多控件,每个控件都有三五十个属性,如果这些属性都是普通属性,那么每个控件就有三五十个字段在内存中,虽然你根本没有设置过它们,但它们依然存在(占内存)
FontSize就是例子,一般你很少设置它。这样对内存是一种浪费。而依赖属性只有你设置了才会在内存中存在,不设置就不会存在。这样就节省了空间,如何节省后面会说到。

再有,依赖属性可以应用动画,双向绑定,Style,还可以从可视化树的祖先中继承值,等等很多好处。 

 

(三) 依赖属性是如何实现这些好处的?
从上面依赖属性的定义方式可以看出,最重要的就是两个类DependencyPropertyDependencyObject,后者有两个重要的方法(GetValue和SetValue),

我们来模拟一下这两个类的实现。 

 

1. 先从简单的来:DependencyProperty类,代码很简单,如下:

public class DependencyProperty

{
    private Type _propertyType;
    
private Type _ownerType;
    
private string _name;
    
private object _defaultValue;

    
public object DefaultValue { get { return _defaultValue; } }
    
public string Name { get { return _name; } }

    
private DependencyProperty(string name, Type propertyType, Type ownerType, object defaultValue)
    {
        _propertyType = _propertyType;
        _ownerType = ownerType;
        _name = name;
        _defaultValue = defaultValue;
    }

    
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, object defaultValue)
    {
        
if (defaultValue.GetType() != propertyType)
        {
            
throw new Exception(string.Format("the type of defaultValue is not {0}", propertyType.Name));
        }

        DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultValue);
            
        
//再做一些其它的事情,好让dp能获得一些WPF的功能
        
//可能会用到 ownerType
            
        
return dp;
    }
}

 

说明:在这里,DependencyProperty没做什么事,只是把Name和缺省值保存起来而已。

 

 

2. 接下来,模拟一下DependencyObject类的实现,代码不难,如下:

public class DependencyObject 

{
    private IDictionary<DependencyProperty, object> _dict = new Dictionary<DependencyProperty, object>();
        
    
public void SetValue(DependencyProperty p, object val)
    {
        
if (_dict.ContainsKey(p))
        {
            _dict[p] = val;
        }
        
else
        {
            _dict.Add(p, val);
        }
    }

    
public object GetValue(DependencyProperty p)
    {
        
//如果被动画控制,返回动画计算值。(可能会用到p.Name)

        
//如果有本地值,返回本地值
        if(_dict.ContainsKey(p))
        {
            
return _dict[p];
        }

        
//如果有Style,则返回Style的值
        
        
//返回从可视化树中继承的值

        
//最后, 返回依赖属性的DefaultValue
        return p.DefaultValue;
    }
}

说明

DependencyObject用了一个字典来存储属性值。如果你从来没设置过MyTextBlock的FontSize,也就从来没调用过DependencyObject的SetValue方法,字典中也就不会有FontSize的值。从而节省了空间(要知道MyTextBlock会有几十个依赖属性)。
DependencyObject的GetValue方法,根据WPF的优先级规则,分别检查动画值,本地值(字典中的值),Style值,如果都没有就返回缺省值。也就是说SetValue方法做了很多事情,从而让依赖属性比普通属性强大了很多

3. 可以写一小段代码验证一下:如果没设置FontSize,则FontSize返回缺省值,代码很难,如下: 

class Program

{
    static void Main(string[] args)
    {
        
//不设置txt的FontSize属性
        MyTextBlock txt = new MyTextBlock();
        Console.WriteLine(txt.FontSize);

        
//设置FontSize属性
        txt.FontSize = 18;
        Console.WriteLine(txt.FontSize);
    }
}

 

 最后,上面只是对DependencyPropertyDependencyObject的猜测。

 如果你觉得讲得还是罗嗦,那就也写一篇吧。

 

 

作者: 吾爱孟夫子 发表于 2011-08-17 21:30 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"