1、Field:字段,封装在类中的变量。
Method:方法,封装在类中的函数。
成员:类中的字段和方法,可分为静态成员和非静态成员。
静态字段在内存中只有一份拷贝。
非静态字段是每个实例拥有一个拷贝。
方法无论是否静态,在内存中都只有一份拷贝。
2、CLR属性
直接把字段的数据暴露给外界的作法很不安全,我们希望对象自己有能力判断将要写入的字段值是否正确。于是提供了Set和Get方法来对private的字段进行读取,这就是private字段的安全包装。.Net Framework将Get/Set方法合并成了属性(Property),也称为CLR属性。
属性的编译结果实际上是两个方法(Get/Set),在内存中只有一份拷贝,不会增加内存负担。
3、依赖属性
一种可以自己没有值,并能通过Binding从数据源获得值得属性。
4、依赖对象(Dependency Object)
对象在被创建时并不包含用于存储数据的空间,只保留在需要用到数据时能够获得默认值、借用其他对象数据、或实时分配空间的能力,这种能力依靠依赖属性(Dependency Property)来实现。
WPF中所有UI控件都是依赖对象。
5、依赖属性的包装器(wrapper)是一个CLR属性。包装器以“实例属性”的形式向外界暴露依赖属性,这样一个依赖 属性才能成为数据源的Path。
6、自定义一个依赖对象并声明一个依赖属性。
1 public class Student:DependencyObject 2 { 3 public Student() 4 { 5 6 } 7 8 public static readonly DependencyProperty NameProperty= 9 DependencyProperty.Register("Name",typeof(string),typeof(Student)); 10 }
7、直接使用上面的依赖属性。通过依赖对象的GetValue和SetValue方法对依赖属性的值进行读写。
1 Student stu=new Student(); 2 stu.SetValue(Student.NameProperty,this.textBox1.Text); 3 this.textBox2.Text=(string)stu.GetValue(Student.NameProperty);
8、直接使用GetValue和SetValue方法略不方便,因此大多数情况下我们会为依赖属性添加一个CLR属性外包装。有了这个包装,依赖对象就有了用于暴露数据的Binding Path。
1 public string Name 2 { 3 get{return (string)GetValue(NameProperty);} 4 set{SetValue(NameProperty,value);} 5 }
依赖属性自带NorifyPropertyChanged的功能。
9、声明依赖属性的snippet:输入propdp,连续按两次Tab键,就出现了标准依赖属性的代码段。
10、依赖属性的创建和注册过程:
创建一个依赖DependencyProperty实例,用它的CLR属性名和宿主类型名生成HashCode,最后把HashCode和DependencyProperty实例作为Key-Value对存到全局的名为PropertyFromName的HashCode中。最后,生成的DependencyProperty实例作为返回值返回。
每个DependencyProperty实例都有一个唯一的GlobalIndex的int类型属性,这个值才是DependencyProperty实例的哈希值,通过这个值可以直接检索到该实例。
11、依赖属性如何存储值
每个DependencyObject实例都自带一个EffectiveValueEntry类型数组,而每个EffectiveValueEntry的PropertyIndex属性的值就是DependencyProperty实例的GlobalIndex。
当某个依赖属性的值要被读取时,算法会从EffectiveValueEntry[]数组中检索值,如果没有值,就会返回依赖属性的默认值。
被static readonly关键字修饰的依赖属性对象的作用是用来检索真正的属性值而不是存储值。
12、附加属性
snippet:propa
声明一个附加属性:
1 class School:DependencyObject 2 { 3 4 public static int GetGrade(DependencyObject obj) 5 { 6 return (int)obj.GetValue(GradeProperty); 7 } 8 9 public static void SetGrade(DependencyObject obj, int value) 10 { 11 obj.SetValue(GradeProperty, value); 12 } 13 14 // Using a DependencyProperty as the backing store for Grade. This enables animation, styling, binding, etc... 15 public static readonly DependencyProperty GradeProperty = 16 DependencyProperty.RegisterAttached("Grade", typeof(int), typeof(ownerclass), new PropertyMetadata(0)); 17 18 }
使用附加属性:
1 Student stu = new Student(); 2 School.SetGrade(stu, 6); 3 int grade = School.GetGrade(stu);
附加属性的本质是依赖属性,也可以使用Binding依赖在其他对象的数据上。
13、路由事件(RoutedEvent)
与依赖属性类似,每个路由事件都有一个自己的CLR事件。
监听和处理路由事件的方法:
C#:
this.grid.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.ButtonClicked));
XAML:
<Grid x:Name="gridRoot" Button.Click="ButtonClicked"> </Grid>
14、RoutedEventArgs的两个属性
Source:LogicalTree上的消息源头
OriginalSource:VisualTree上的消息源头
15、附加事件
附加事件的宿主没有可视化实体的界面元素。比如,设计一个Student类,当Student实例的属性值发生变化时就激发一个路由事件。为了发送路由事件,可以借用Button等UI元素的RaiseEvent()方法。
定义附加事件:
1 class Student 2 { 3 public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRoutedEvent 4 ("NameChanged",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(Student)); 5 6 public static void AddNameChangedHandler(DependencyObject d, RoutedEventHandler h) 7 { 8 UIElement e = d as UIElement; 9 if (e != null) 10 { 11 e.AddHandler(Student.NameChangedEvent, h); 12 } 13 } 14 15 public static void RemoveNameChangedHandler(DependencyObject d, RoutedEventHandler h) 16 { 17 UIElement e = d as UIElement; 18 if (e != null) 19 { 20 e.RemoveHandler(Student.NameChangedEvent, h); 21 } 22 } 23 public int Id { get; set; } 24 public string Name { get; set; } 25 }
16、附加事件的路由第一站是激发它的元素(如上例中的button)
17、实际上,附加事件一般都是定义在像Binding、Mouse、KeyBoard等全局的Helper类中。
18、路由命令(RoutedCommand)
使用命令可以避免自己写代码去判断Button是否可用以及添加快捷键。
命令目标发送路由事件CanExecute和Executed等,这些路由事件沿着UI元素树向上传递并被CommandBinding捕捉。