一、属性
- 类最初只有字段与函数,字段为一个变量,访问权限可以是private,protected,public。而将字段设为private,不方便外界对类数据的操作,但是将字段设为public又怕外界对数据进行非法操作,于是便为每个字段设计了set和get,在保证数据的合法性的同时支持外界对其进行操作。
- 因为字段有了对应的访问接口,便升级成为了属性。注意类函数是静态的,只有一份,因此类实例增多时,类的字段变为属性的内存存储空间不会增加,即同个字段的{set;get;}使用的是同一份。
- private int id;
public int Id { set; get; }
二、依赖属性
1. 依赖属性的定义
- 依赖属性就是一种可以自己没有值,并能通过使用Binding从数据源获得值(依赖在别人身上)的属性,而拥有依赖属性的对象被称为“依赖对象”。
- 与传统的CLR属性相比,依赖属性的优点有:1.节省实例对内存的开销 2. 属性值可以通过Binding依赖在其他对象上。
2. 依赖属性对内存的使用
- WPF允许依赖对象在被创建的时候并不包含用于存储数据的空间、只保留在需要用到数据时能够获得默认值、借用其他对象数据或实时分配空间的能力。
- WPF中依赖对象的概念被DependdencyObject类实现,依赖属性的概念则由DependencyProperty类所实现。DependdencyObject通过GetValue和SetValue来获取和设置依赖属性的值。
3. 声明和使用依赖属性
public class Student:DependencyObject
{
public static readonly DependencyProperty=
DependencyProperty.Register("Name",typeof(string),typeof(Student));
}
声明:
1. 依赖属性一定要在依赖对象DependencyObject内声明
2. 成员变量名称后面需要加Property以标识其为依赖属性
3. 需要同时被public static readonly三个修饰符修饰
4. 并非使用new操作符得到而是使用DependencyProperty.Register方法生成
-
依赖属性是由public static readonly修饰的DependencyProperty实例,没有包装器(set;get)这个依赖属性依旧存在
-
包装器的作用是以“实例属性”的形式向外界暴露依赖属性,这样依赖属性才能成为Binding的Path
-
注册依赖属性使用的第二个参数是一个数据类型,这个数据类型也是包装器的数据类型
-
在没有对依赖属性使用包装器时,外界只能通过类实例的GetValue()和SetValue()来对依赖属性进行值的设置与获取。
-
若为依赖属性添加一个CRL属性外包装,则外界可以直接访问依赖属性。
public class Student:DependencyObject
{
public string Name
{
get{ return (string}GetValue(NameProperty);}
set{ SetValue(NameProperty,value);}
}
}
值得注意的是,Student类虽然没有实现InoticePropertyChanged接口,但是当属性的值发送改变时与之关联的Binding对象依然可以得到通知,依赖属性默认带有这样的功能,因此会是很好的Binding数据源。
4. 依赖属性值存储的秘密
为什么依赖属性被public static readonly修饰?
- 系统维护一个全局静态的依赖属性实例哈希表。
- 依赖对象实例的属性名和宿主类型名生成hash code为Key,依赖对象实例为Value,被存入哈希表中。
- 每个依赖属性实例会有一个GlobalIndex属性,系统通过该值的唯一性可以在哈希表中找到对应的EffectiveValueEntry来获取值。
- 每个依赖对象实例都会维护一个EffectValueEntry[],存储不同的依赖属性实例的GlobalIndex,为检索到实例对应属性的值而存在。
- 总的来说,static关键字所修饰的依赖属性对象其作用是用来检索真正的属性而不是存储值,被用来检索值的实际上是依赖属性的GlobalIndex属性。
依赖属性的值除了可能存储在默认值和EffectiveValueEntry[]外,还有很多途径可以获得,但是它们有优先级控制。
三、附加属性
1. 附加属性的定义
- 附加属性是指一个属性本来不属于某个对象,但由于某种需求而被后来附加上。即把对象放入一个特定环境后对象才具有的属性(表现出来就是被环境赋予的属性)就被称为附加属性。
- 比如说,TextBox在Grid下具有Grid.Column、Grid.Row等属性,在Canvas在具有Canvas.Top、Canvas.Left等属性,在DockPanel下具有DockPanel.Dock属性。需要注意的是,附加属性的真实所有者不是TextBox,而是Grid、Canvas、DockPanel。
2. 附加属性的声明
附加属性的作用就是将属性与数据类型(宿主)解耦,让数据类型的设计更加灵活,其本质就是依赖属性,二者仅在注册和包装器上有一点区别。声明附加属性与依赖属性及其相似,唯一的不同就是注册附加属性使用的是名为RegisterAttached的方法,但参数却与使用Register方法无异。
class School:DependencyObject
{
public static int GetGrade(DependencyObject obj)
{
return (int)obj.GetValue(GradeProperty);
}
public static void SetGrade(DependencyObject obj,int value)
{
obj.SetValue(GradeProperty,value);
}
public static readonly DependencyProperty GradeProperty=
DependencyProperty.RegisterAttached("Grade",typeof(int),typepf(School),new UIPropertyMetaData(0));
}
3.附加属性的使用
如何消费附加属性呢?直接使用宿主的GetPropertyName和SetPropertyName将值赋值给某个类对象。值仍然被保存在Human实例的EffectiveValueEntry数组里,只是用于在数组里检索值的依赖属性(即附加属性)并不以Human类为宿主而是寄宿在School类里。
private void Button_Click(object sender,RoutedEventArgs e)
{
Human human=new Human();
School.SetGrade(human,6);
int grade=School.GetGrade(human);
MessageBox,Show(grade.ToStrin());
}
4. 附加属性的依赖绑定
- 最后,因为附加属性其本质是依赖属性,所以它的值也可以使用Binding依赖在其他对象的数据上。