《深入浅出WPF》学习笔记之深入浅出话属性
依赖属性是一种可以从父级元素继承,并且可以通过Binding从数据源获取,当从父级继承时不占用内存的属性系统。拥有依赖属性的对象称为依赖对象。WPF允许在创建对象时并不分配用于存储数据的空间,而在需要时实时分配空间或直接借用父级数据,这种对象称为依赖对象(DependencyObject),这种实时获取数据的能力依靠依赖属性(Dependency Property)来实现。必须使用依赖对象作为依赖属性的宿主。DependencyObject是相当底层的一个基类,通过GetValue和SetValue获取或设置依赖属性的值。
声明和使用依赖属性
public class Student:DependencyObject { public readonly static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(String), typeof(Student)); public String Name { get { return (String)GetValue(Student.NameProperty); } set { SetValue(Student.NameProperty, value); } } }
依赖属性一定被声明为public static readonly,并且约定依赖属性成员名称添加后缀“Property”表明这是一个依赖属性。依赖属性实例通过Register方法创建,Register的第一个参数为此依赖属性的CLR属性包装器的名称,第二个参数为依赖属性注册的类型,第三个参数为依赖属性的宿主类型。当依赖属性没有用CLR属性包装时,只能作为数据绑定的Target,使用CLR属性包装后,即可以作为Binding的Target,也可以作为Binding的Source。当依赖属性作为Binding的Source时,即使不实现INotifyPropertyChanged接口在属性值更改时也能通知Binding更改Target,依赖属性自带通知功能,因此当我们不想实现繁琐的INotifyPropertyChanged接口时可以使用依赖属性作为数据源。在Visual Studio中通过输入propdp在连按两次Tab键可以直接输入依赖属性模板,以加快编码速度。
依赖属性值存取的原理
创建一个DependencyProperty实例,并用它的CLR属性名和宿主类型名生成hash code,最后使用hash code作为DependencyProperty实例的key存入全局的名为DependencyProperty.PropertyFromName的Hashtable中。这样通过CLR属性名和宿主类型名检索出DependencyProperty实例,因此在同一个类型中不能声明CLR属性名相同的多个依赖属性实例。
依赖属性的值被统一存储在DependencyObject中的EffectiveValueEntry[] _effectiveValues这样的数组中,数组根据算法和DependencyProperty的GlobalIndex属性进行排序,依赖属性值的存取、删除、等操作都是针对这个数组进行,DependencyProperty的实例只是依赖属性值的标识,用来检索属性值。
附加属性
顾名思义,附加属性是说一个属性本来不属于某个类,但由于某种需求而被后来附加上。也就是说把对象放到一个特殊的环境中对象才有的属性。附加属性的作用就是让属性与数据类型解耦,让数据类型的设计更加灵活,附加属性定义在更高层次语义的对象上,在需要时附加到与之关联的对象上。附加属性本质上是依赖属性,二者仅在注册与包装器上有区别。
public 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); } // Using a DependencyProperty as the backing store for Grade. This enables animation, styling, binding, etc... public static readonly DependencyProperty GradeProperty = DependencyProperty.RegisterAttached("Grade", typeof(int), typeof(School), new PropertyMetadata(0)); }
声明附加属性时仅注册方法不同,参数和参数含义相同。附加属性的包装器与依赖属性不同,分别声明Get和Set方法用来存取附加属性,附加属性存取在要附加的对象上。实际上附加属性就是使用定义在其他类中的DependencyProperty存取属性值。附加属性的本质为依赖属性,因此可以使用Binding依赖其他对象的属性值。示例代码:
<Slider x:Name="slider" Minimum="0" Maximum="100"></Slider> <Canvas Width="200" Height="200" Background="Gray"> <Rectangle x:Name="rect" Width="20" Height="20" Canvas.Left="{Binding ElementName=slider, Path=Value,Mode=OneWay}" Fill="Red"></Rectangle> </Canvas> //等价的后台代码 Binding binding = new Binding("Value"); binding.ElementName = "slider"; binding.Mode = BindingMode.OneWay; rect.SetBinding(Canvas.LeftProperty, binding);