如果看文字版本不没耐心, 点击下方查看视频讲解。
点击观看视频
在讲解依赖属性之前, 首先我们熟悉一下WPF当中的绑定(Binding), 可能你曾用过WPF中绑定的语法。
下面演示了在Button按钮上为Content属性设置了一个绑定语法, 如下所示:
<Button Content="{Binding Content}"/>
当你在Content属性按下F12转到定义时,可以观察到Button按钮所继承的类的定义,如下所示:
如图上红圈位置, 定义了一个静态的只读字段ContentProperty。
通过查看该字段的类型DependencyProperty,没错!它就是一个依赖属性。
看到这里, 你应该有了一个概念, 在WPF当中, 所有支持绑定的属性本质上它都是封装后的依赖属性。
那么也就是说, 只有依赖属性才可以进行绑定。
如果你不能够理解, 我们再举个例子, 当你使用WPF当中PasswordBox控件的时候, 你会发现Password属性不支持绑定, 当按下F12转到定义,会发现该Password就是一个普通属性:
这也就是为什么Password不支持绑定的真正原因。
现在, 我们来解决另外一个概念问题, 可能看到上面, 你还是不太清楚属性和依赖属性它们的区别在哪里?
很常见, 在C#中的标准属性,通常会由一个非静态类型的私有字段支持, 假设当前有一个对象, 它拥有100个标准属性,并且背后都定义了一个4字节的字段, 如果我们初始化10000个这样的对象, 那么这些字段将占用100×4×10000= 3.81M 内存。但是实际上, 我们并非使用到所有的属性, 这就意味着大多数内存会被浪费!
如何解决属性带来的问题? 我们回到现实生活当中想象一种场景, 假设老王和你的女朋友去旅游, 他们准备东西的时候大都是把必要的带上, 而不是说女朋友想喝水,要不然在行李箱里面放一箱水? 那么是不是意味着上厕所把纸带上? 洗发水? 沐浴露? 天呐, 这真是一场糟糕的旅行。
我们都知道, 水、厕所纸、洗发水、沐浴露这些酒店里面都有阿, 为什么要我们自己带? 所以我们懂了, 这些不必要带的东西我们可以依赖外部提供给我们。是的, 我们把这种思想带到编程当中。
所以, 这就是WPF当中的依赖属性的理念, 也许你在其它的地方都听过别人讲解过依赖属性, 并且他们都告诉你依赖属性本身没有值, 可以依赖绑定来源获得值。
下面是对比属性/依赖属性改的定义:
//依赖属性
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(Test), new PropertyMetadata(0));
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
//属性
private int name;
public int Name
{
get { return name; }
set { name = value; }
}
通过上面我们可以了解到, 普通的属性通过定义了一个私有字段, 然后通过这个私有字段进行读取或设置值。
而对于依赖属性, 它同样是使用属性的访问方式, 但是它获取和设置值是通过依赖属性而不再是私有字段。
同样, 你也许在许多的博客当中, 别人教你怎么定义依赖属性, 它通过DependencyProperty类的静态Register方法注册, 如下所示:
public class Test : DependencyObject
{
public string Message
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("Message", typeof(string), typeof(Test), new PropertyMetadata(null));
}
上面的代码当中, 我们定义了一个string类型的依赖属性Message, 如果你们有看过WPF的源代码, 你可能会了解, 在其背后, 生成了一个key/value存储在Hashtable里面。
终于解开了疑惑,有了这个全局的Hashtable存在, 意味着,他就是用来存储依赖属性实例的地方。
注意:定义依赖属性的地方, 你会发现它需要继承于DependencyObject对象, 通过该对象当中的提供的GetValue/SetValue方法, 我们可以获取/设置依赖属性的值:
看看WPF中的继承关系, 噢,懂了。 基类就是DependencyObject。
噢! 我的, 这也就是为什么, 微软提供了标准属性的访问方式!让我们傻傻分不清其内部通过依赖属性来设置或获取值。
在线搜索WPF源代码
字母意思来理解, 附加属性就是对于一个对象而言, 本来它不具备这个属性, 但是由于附加给这个对象, 然后才有了这个属性,这种我们称之为附加属性。
注:附加属性也是依赖属性, 只是它的注册方式与表达方式略有不同。
如果仍然不理解, 我们可以找到WPF当中经常用到的例子, 如下所示, 当一个按钮处在不同的容器当中, 它就具有了不同的附加属性:
<Grid>
<Button Grid.Row="0" Grid.Column="0" />
Grid>
<DockPanel>
<Button DockPanel.Dock="Left" />
DockPanel>
<Canvas>
<Button Canvas.Left="10"/>
Canvas>
我们可以输入propdp再按两下tab键生成一个依赖属性的模板,如下所示:
注: 声明依赖属性的所在位置的对象必须直接或简介继承于DependencyObject对象, 这样它才具备GetValue/SetValue方法。
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
我们可以输入propa再按两下tab键生成一个附加属性的模板,如下所示:
注:声明附加属性的对象无需继承于DependencyObject, 因为这个时候DependencyObject对象作为方法参数传递。
public static int GetMyProperty(DependencyObject obj)
{
return (int)obj.GetValue(MyPropertyProperty);
}
public static void SetMyProperty(DependencyObject obj, int value)
{
obj.SetValue(MyPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
依赖属性: 当您需要单独创建控件时, 并且希望控件的某个部分能够支持数据绑定时, 你则可以使用到依赖属性。
附加属性: 这种情况很多, 正因为WPF当中并不是所有的内容都支持数据绑定, 但是我们希望其支持数据绑定, 这样我们就可以创建基于自己声明的附加属性,添加到元素上, 让其元素的某个原本不支持数据绑定的属性间接形成绑定关系。
例如:为PassWord定义附加属性与PassWord进行关联。例如DataGrid控件不支持SelectedItems, 但是我们想要实现选中多个条目进行数据绑定, 这个时候也可以声明附加属性的形式让其支持数据绑定。
本人致力于建筑土木行业与计算机交叉融合,欢迎加入建筑与编程qq群,共同探讨技术难题,群号:516270086