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

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

注册方法

我们知道注册依赖属性使用Register方法,注册附加属性则使用RegisterAttached方法,如下代码

public class DPCustomPeople:DependencyObject
{   public static readonly DependencyProperty AgeProperty =DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople));public static readonly DependencyProperty Age2Property =DependencyProperty.RegisterAttached("Age2", typeof(int), typeof(DPCustomPeople));
}

包装属性

public int Age
{get { return (int)GetValue(AgeProperty); }set { SetValue(AgeProperty, value); }
}public int Age2
{get { return (int)GetValue(Age2Property); }set { SetValue(Age2Property, value); }
}

一般默认依赖属性使用CLR属性进行包装,附加属性使用Get,Set方法进行包装. 
下面我则均以属性进行包装,从表面上看两者除了方法不同,其他都是一样的 
那么附加属性的魔力到底何在呢?

XAML的魔力

public class AttachEntity
{public static double GetWidth(DependencyObject obj){return (double)obj.GetValue(Button.WidthProperty);}public static void SetWidth(DependencyObject obj, double value){obj.SetValue(Button.WidthProperty, value);}
}

考虑以上代码,你可能从来都没有试过在没有附加属性的情况下,下面的只有Get,Set的方法,然后尝试在XAML中设置

<Button local:AttachEntity.Width="300" Content="Button" />

奇迹出现,赋值成功,所以我们一直以来都误解了附加属性. 

即来看到这里,我们不妨试试依赖属性

public static readonly DependencyProperty AgeProperty =DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople),new UIPropertyMetadata(0,(sender,args)=>{var element = sender as DPCustomPeople;}));public static int GetAge(DependencyObject obj)
{return (int)obj.GetValue(AgeProperty);
}public static void SetAge(DependencyObject obj, int value)
{obj.SetValue(AgeProperty, value);
}
<Button local:DPCustomPeople.Age="2" Content="Button" Name="button1"/>

程序运行正常,发生下列情况

  • 可以取到附加属性值
  • 属性变更通知没有发生

默认属性元数据

现在是时候来看看依赖属性与附加属性的区别了,以下是内部注册方法

第一段:注册依赖属性的方法

public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
{RegisterParameterValidation(name, propertyType, ownerType);PropertyMetadata defaultMetadata = null;if ((typeMetadata != null) && typeMetadata.DefaultValueWasSet()){defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);}DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);if (typeMetadata != null){property.OverrideMetadata(ownerType, typeMetadata);}return property;
}

第二段:注册附加属性方法

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{RegisterParameterValidation(name, propertyType, ownerType);return RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
}

请先忽略上面划线部分的代码,现在我们看到在注册依赖属性时,前后多了一些处理.

原来是属性元数据在作怪。

注册依赖属性时,会传入一个属性元数据,但内部定义了一个默认的属性元数据(defaultMetadata ),当依赖属性注册完毕后,则重写了属性元数据(OverrideMetadata),而注册附加属性时,则直接传入参数.这个参数则直接作为了依赖属性的默认元数据,如下代码

var people = new DPCustomPeople();
var defaultMetadata=DPCustomPeople.AgeProperty.DefaultMetadata;
var metadata = DPCustomPeople.AgeProperty.GetMetadata(people);

附加属性只有默认属性元数据,根据以上源码,我们甚至可以改造附加属性为依赖属性,下面的附加属性则变成了依赖属性

注意点: 
(1)重写属性元数据是一个合并的过程,所以重写的变更事件并不会触发 
(2)若属性元数据已经注册完毕,同个类型的属性元数据不可重复重写 
(3)默认属性元数据无法更改

public static readonly DependencyProperty Age3Property;
static DPCustomPeople()
{var metaData = new PropertyMetadata(0, new PropertyChangedCallback((sender, args) =>{MessageBox.Show("hello1");}));PropertyMetadata defaultMetadata = null;if ((metaData != null)){defaultMetadata = new PropertyMetadata(metaData.DefaultValue, new PropertyChangedCallback((sender, args) =>{MessageBox.Show("hello2");}));}Age3Property = DependencyProperty.RegisterAttached("Age3", typeof(int), typeof(DPCustomPeople), defaultMetadata);Age3Property.OverrideMetadata(typeof(DPCustomPeople), metaData);
}

属性元数据重写的补充

http://www.cnblogs.com/Clingingboy/archive/2010/02/02/1661842.html 
在之前有介绍过属性元数据的部分,这里做一个补充.上面第三点已经列出来了. 
(1)每个DP都会有一个默认属性元数据,依赖属性默认属性元数据由内部创建,其又根据我们传入的参数,同时创建了一个属性元数据.也就是说依赖属性拥有两个属性元数据.依赖属性对于同一对象是无法重写属性元数据的.下面则报错.

            Age3Property = DependencyProperty.Register("Age3", typeof(int), typeof(DPCustomPeople), defaultMetadata);Age3Property.OverrideMetadata(typeof(DPCustomPeople), metaData);//error

(2)同理,由于附加属性注册时只拥有一个默认属性元数据,所以其初始化时就可以对同类型的对象进行重写(就是上面的例子) 
     注意:重写属性元数据时并不会与默认属性元数据合并,所以附加属性注册时,若有回调方法,总是会触发的

重写属性元数据规则

比如在重写属性元数据时重新定义了一个回调方法,其是一个合并过程,并不会覆盖父类的回调方法.如果你想改变重写规则的话可以重写

PropertyMetadata的Merge方法,如下则不会触发父类的回调方法.这看需求而定

public class CustomPropertyMetadata : PropertyMetadata
{public CustomPropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback){this.DefaultValue = defaultValue;this.PropertyChangedCallback = propertyChangedCallback;}protected override void Merge(PropertyMetadata baseMetadata, DependencyProperty dp){var a = this.PropertyChangedCallback;base.Merge(baseMetadata, dp);this.PropertyChangedCallback = a;}
}

改写属性元数据

默认有两种方法为一个元素添加ToolTip

<Button ToolTipService.ToolTip="Test">Button</Button>
<Button ToolTip="Test">Button</Button>

两者效果是相同的,ToolTip其内部还是设置了ToolTipService.ToolTip属性

public object ToolTip
{get{return ToolTipService.GetToolTip(this);}set{ToolTipService.SetToolTip(this, value);}
}

注意:改写属性元数据并非改写依赖属性 
参考:http://www.cnblogs.com/yayx/archive/2008/06/03/1213126.html

(1)属性元数据的改写 如Control的BackgroundProperty则来自Panel的BackgroundProperty的改写,下面设置效果相同

this.SetValue(Panel.BackgroundProperty, Brushes.Red);

(2)改写依赖属性元数据

ToolTipProperty = ToolTipService.ToolTipProperty.AddOwner(typeof(DPCustomPeople));

其为设置属性的时候提供了方便,隐藏了ToolTipService的存在,其实不设置并不会怎么样. 
如内部的ContextMenuProperty

ContextMenuProperty = ContextMenuService.ContextMenuProperty.AddOwner(typeof(DPCustomPeople), new FrameworkPropertyMetadata(null));

下面两者取属性是等价的

public ContextMenu ContextMenu
{get{return (GetValue(ContextMenuProperty) as ContextMenu);}set{SetValue(ContextMenuProperty, value);}
}public ContextMenu ContextMenu
{get{return ContextMenuService.GetContextMenu(this);}set{SetValue(ContextMenuService.ContextMenuProperty, value);}
}

那么改写属性元数据到底做了什么? 
改写实质上一个重写属性元数据的过程,区别在OwnerType发生了变化,当OwnerType是继承关系的话,那么属性元数据则进行合并,否则的话则会为该OwnerType创建一个新的属性元数据.
 

更新于:2010/8/4

经过上面的推敲,我们可以看到依赖属性与附加属性的区别在于属性元数据的变化,附加属性也变的不再那么神奇了.下面欢迎你加入讨论中来.

原文地址:

http://www.cnblogs.com/Clingingboy/archive/2010/06/11/1756500.html

转载于:https://www.cnblogs.com/fxf568/archive/2012/10/10/2717812.html

Silverlight的依赖属性与附加属性(六)相关推荐

  1. wpf 依赖属性和附加属性

    原文:wpf 依赖属性和附加属性 1.依赖属性 解释:依赖属性是配合binding出现的产物,功能主要是配合binding. 作用: 一.当自定义usercontrol时,需要与宿主(父窗体)双向绑定 ...

  2. Wpf依赖属性和附加属性在样式中的应用

    文章目录 前言 一.依赖属性的使用方法 1.添加派生类(以Button为例) 2.修改样式模板 3.前台应用 二.依赖属性的使用方法 1.添加普通类 2.修改模板 3.前台使用 总结 前言 在wpf的 ...

  3. WPF之依赖属性和附加属性

     参考资料: 一站式WPF--依赖属性(DependencyProperty)一 一站式WPF--依赖属性(DependencyProperty)二         依赖属性之我见: 这两篇文章介绍的 ...

  4. WPF的依赖属性和附加属性(用法解释较全)

    转:https://www.cnblogs.com/zhili/p/WPFDependencyProperty.html 一.引言 感觉最近都颓废了,好久没有学习写博文了,出于负罪感,今天强烈逼迫自己 ...

  5. [UWP]依赖属性2:使用依赖属性

    原文:[UWP]依赖属性2:使用依赖属性 5. 完整的自定义依赖属性 5.1 定义 /// <summary> /// 标识 Title 依赖属性. /// </summary> ...

  6. UWP WP8.1 依赖属性和用户控件 依赖属性简单使用 uwp添加UserControl

    上面说 附加属性.这章节说依赖属性. 所谓依赖属性.白话讲就是添加一个公开的属性. 同样,依赖属性的用法和附加属性的用法差不多. 依赖属性是具有一个get,set的属性,以及反调函数. 首先是声明依赖 ...

  7. 34.Silverlight中不得不了解使用的依赖属性

    Silverlight中我们经常使用自定义控件,并且在自定义控件中制作自定义属性,在项目中大量的创建这个自定义控件,每次都需要占用内存来创建这个 属性的默认值.可很多时候我们都只需要去使用这个属性的默 ...

  8. Silverlight:Dependency Property(依赖属性)学习笔记

    学习SL/WPF,Dependency Properties(依赖属性)是一个全新(陌生)但又无法回避的概念. http://www.wpftutorial.net/DependencyPropert ...

  9. WPF基础到企业应用系列8——依赖属性之“风云再起”

    一. 摘要 首先圣殿骑士很高兴"WPF 基础到企业应用系列" 能得到大家的关注.支持和认可.看到很多朋友留言希望加快速度的问题,我会尽力的,对你们的热情关注也表示由衷的感谢.这段时 ...

  10. 依赖属性之“风云再起”三

    八. DependencyObject测试代码 在写DependencyObject测试代码之前,我们先看一下它到底有哪些成员和方法,如下图: 通过上面的这幅图,我们知道它的主要功能包括:各种依赖属性 ...

最新文章

  1. win8/Metro开发系列二 Xaml数据绑定
  2. Android真机调试打印日志的方法
  3. 广告影响网站打开速度解决方案
  4. 【机器视觉】 Halcon代码导出高级语言代码
  5. 如何进入embl的ebi网站fasta3服务器,The EMBL-EBI bioinformatics web and programmatic tools framework...
  6. 2018.4.23 数据结构
  7. python装饰器函数执行后日志_Python装饰器记录日志、异常处理、函数添加,python,处理函数,功能...
  8. 使用python操作word
  9. Tex, LaTex概念及实例
  10. php自动跳转函数,迅睿CMS 重写控制网站自动跳转函数
  11. Python数据可视化-matplotlib and seaborn
  12. WebSocket心跳检测和重连机制
  13. optuna 自动化调参利器
  14. 【接口测试用例设计思路】
  15. VS2010 正式版 破解方法详解
  16. HTML 语法练习---常见标签
  17. 全桥驱动IR系列参考设计及问题指南
  18. 对勾和叉怎么打_方框里打钩和叉符号怎么输入?N种方法,总有一种适合你!-word里面怎么打勾...
  19. 第十二天内容《基础交换十二》
  20. OpenCV在图片上画线和矩形

热门文章

  1. cv2读取带中文路径方法
  2. Caffe的简介、依赖、框架
  3. Python基于seaborn绘制喜欢的热力图,不同色系一览
  4. c/c++中define用法详解及代码示例
  5. web自动化测试第6步:模拟鼠标操作(ActionChains)
  6. 百度地图依赖包php,调用百度地图
  7. 2021-08-17Cookie 详解
  8. FISCO BCOS简介
  9. 下列属于usb转串口的芯片是_USB转串口常用芯片
  10. 二十五、JAVA多线程(三、线程同步)