Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR)属性的功能,这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性。
这段是MSDN上对依赖属性(DependencyProperty)的描述。主要介绍了两个方面,WPF中提供了可用于扩展CLR属性的服务;被这个服务支持的属性称为依赖属性。
单看描述,云里雾里的,了解一个知识,首先要知道它产生的背景和为什么要有它,那么WPF引入依赖属性是为了解决什么问题呢?
属性是我们很熟悉的,封装类的字段,表示类的状态,编译后被转化为get_,set_方法,可以被类或结构等使用。 一个常见的属性如下:
1: public class NormalObject
2: {
3: private string _unUsedField;
4:
5: private string _name;
6: public string Name
7: {
8: get
9: {
10: return _name;
11: }
12: set
13: {
14: _name = value;
15: }
16: }
17: }
在面向对象的世界里,属性大量存在,比如Button,就大约定义了70-80个属性来描述其状态。那么属性的不足又在哪里呢?
当然,所谓的不足,要针对具体环境来说。拿Button来讲,它的继承树是Button->ButtonBase->ContentControl->Control->FrameworkElement->UIElement->Visual->DependencyObject->…
每次继承,父类的私有字段都被继承下来。当然,这个继承是有意思的,不过以Button来说,大多数属性并没有被修改,仍然保持着父类定义时的默认值。通常情况,在整个Button对象的生命周期里,也只有少部分属性被修改,大多数属性一直保持着初始值。每个字段,都需要占用4K等不等的内存,这里,就出现了期望可以优化的地方:
回过头来,总结一下依赖属性的优点:
借助于依赖属性,WPF提供了强大的属性系统,可以支持数据绑定、样式、动画、附加属性等功能。这篇文章主要是简略的实现了一个从属性到依赖属性的发展过程,当然,具体和WPF的实现还有偏差,希望朋友们都能抓住这个主要的脉络,更好的去玩转它。
除了依赖属性的实现,还有一些很重要的部分,比如借助于依赖属性提出的附加属性,以及如何利用依赖属性来更好的设计实现程序,使用依赖属性有哪些要注意的地方。呵呵,那就,下篇吧。
有一个小技巧,需要申明一个依赖属性并使用CLR属性封装时,只需要输入propdp,vs就会给出一个提示,连按两次tab键,一个标准被依赖属性就申明好了,继续按tab键,可以修改依赖属性的各个参数。
首先,属性所在的类要直接或间接继承DependencyObject。这个类生成的对象表示一个具有依赖项属性的对象,这些对象,都能享用WPF的属性系统(属性系统主要是计算属性的值,并提供有关值已更改的系统通知)方面的服务。
这个类有两个比较重要的方法,GetValue(返回当前对象依赖项属性的当前有效值)和SetValue(设置依赖项属性的本地值)。
其实,属性对应的字段必需是公有,静态,只读的,类型为DependencyProperty。即public static readonly DependencyProperty 字段名,同时字段的命名也有规范,属性名+Property,字段在定义时,通过DependencyProperty.Register来实注册属性(只有注册了,才能使用WPF属性系统的服务)。
Register方法有三种重载,如下:
名称 |
说明 |
Register(String, Type, Type) |
使用指定的属性名称、属性类型和属性所在对象的类型。 |
Register(String, Type, Type, PropertyMetadata) |
使用指定的属性名称、属性类型、属性所在对象的类型和属性元数据注册依赖项属性。 |
Register(String, Type, Type, PropertyMetadata, ValidateValueCallback) |
使用指定的属性名称、属性类型、属性所在对象的类型、属性元数据和属性的值验证回调来注册依赖项属性。 |
在Register中,各个参数解释如下:
String:依赖属性的名字(不加Property,即字段的名字);
Type:属性的类型;
Type:属性所属对象的类型;
PropertyMetadata:依赖项对象的属性元数据,是一个PropertyMetadata类型,可能赋初始值。PropertyMetadata有一个object的构造函数;
ValidateValueCallback:表示用作回调的方法,这个类型是一个委托,用于验证依赖项属性的值的有效性,因为是委托,故它的构造参数为一个方法名。
最后,来构造依赖属性,与普通的属性有所区别:
Public 属性类型 属性名
{
Get
{
return (属性类型)this.GetValue(字段名);
}
Set
{
this.SetValue(字段名, value);
}
}
其中的GetValue和SetValue都是调用父类DependencyObject的方法。
完整的代码如下:
代码 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 class MyClass : DependencyObject { public static readonly DependencyProperty MyfieldProperty = DependencyProperty.Register("Myfield", typeof(int),
typeof(MyClass), new PropertyMetadata(0), new ValidateValueCallback(new MyClass().MyValidateMethod)); public int Myfield { get { return (int)GetValue(MyfieldProperty); } set { SetValue(MyfieldProperty, value); } } public bool MyValidateMethod(object value) { return true;//这里实现验证 } }
举例:修改DataGrid的表头
(1)PlotView.xml.cs文件:
public string ColumnName5
{
get { return (string)GetValue(ColumnName5Property); }
set { SetValue(ColumnName5Property, value); }
}
public static readonly DependencyProperty ColumnName5Property =
DependencyProperty.Register("ColumnName5", typeof(string), typeof(PlotView), new UIPropertyMetadata("", new PropertyChangedCallback(ColumnName5PropertyCall)));
private static void ColumnName5PropertyCall(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PlotView send = d as PlotView;
if (send.PlotViewDataGrid.Columns.Count > 6)
{
send.PlotViewDataGrid.Columns[0].Header = (string)e.NewValue;
}
}
(2)MainView.xml文件:
<DataTemplate DataType="{x:Type local:PlotViewModel}" x:Key="PlotTemplate">
<local:PlotView ColumnName5="{Binding ColumnName5}"/>
</DataTemplate>
(3)在PlotViewModel.cs文件
public string ColumnName5
{
get
{
return ((PlotModel)Model).columnName5;
}
set
{
if (((PlotModel)Model).columnName5 != value)
{
((PlotModel)Model).columnName5 = value;
}
RaisePropertyChanged("ColumnName5");
}
}
(4)在MainViewModel.cs文件:
if (true)
{
((PlotViewModel)this.PlotViewModel).ColumnName0 = "时间"
}
else
{
((PlotViewModel)this.PlotViewModel).ColumnName0 = ”名称“
}