我已经阅读了几本关于依赖属性的书籍,但它们都有一个共同点,就是只告诉我们如何实现它们(使用 static readonly DependencyProperty
等),但并没有告诉我们它们从内部如何工作。
我的意思是,它们是以静态方式实现的,但仍适用于所有对象。另一个困惑的点是附加属性。
是否有任何教程可以以简单的方式解释所有这些概念?
我已经阅读了几本关于依赖属性的书籍,但它们都有一个共同点,就是只告诉我们如何实现它们(使用 static readonly DependencyProperty
等),但并没有告诉我们它们从内部如何工作。
我的意思是,它们是以静态方式实现的,但仍适用于所有对象。另一个困惑的点是附加属性。
是否有任何教程可以以简单的方式解释所有这些概念?
我的依赖属性工作模型:
任何 DependencyObject
类都会实现两个特殊的属性。一个是该类的静态属性,它是一个 DependencyProperty
对象字典。每个类实例可以查找该字典以查找有关每个 DependencyProperty
的元信息 - 属性的名称、类型、在 get 和 set 时必须调用的任何回调函数,它如何参与属性继承等等。当您注册依赖属性时,您正在将一个条目添加到这个字典中。
另一个属性是一个实例属性:它是一个字典,由 DependencyProperty
键入,包含每个 DependencyProperty
的本地 值(如果已设置)。
您在 CLR 属性的设置器和获取器中实现的 SetValue
和 GetValue
方法基本上是强制执行的延迟计算。它们不是在后备字段中存储和检索属性的值,而是在值字典中存储和检索属性的值。
依赖属性的魔力在于 GetValue
和 SetValue
实际上做什么。
GetValue
会在对象的值字典中查找属性的值。如果找不到,它会调用父元素的GetValue
以获取父元素认为的值。例如,在窗口中创建TextBox
时,任何查看TextBox
的FontFamily
的内容实际上都是调用了GetValue
。除非您明确设置了字体,否则其字典中没有该属性的条目。因此,GetValue
会向父元素请求该值。父元素可能已经设置或未设置FontFamily
,如果未设置,则它的GetValue
调用返回其父级的值。依此类推,直到到达Window
对象并找到实际的FontFamily
值。TextBox
上设置了FontFamily
,则SetValue
会将该值存储在值字典中。下一次需要获取该TextBox
的FontFamily
值时,GetValue
会在字典中找到该值并返回,因此不需要再次询问父元素。Window
上设置FontFamily
,SetValue
不仅会更新Window
的值字典中的值,还会触发一次属性更改事件,所有依赖于该属性的东西都会听到这个事件。(这就是为什么它们被称为依赖属性的原因)。如果依赖于该属性的对象本身也是依赖属性,则会触发其自己的属性更改事件。这就是为什么更改Window
上的FontFamily
会更改窗口中每个控件的字体,并促使WPF重新呈现更改的控件。附加属性使用相同的方法。任何可以拥有附加属性的对象都有一个字典,其中存储了附加属性的值。当在XAML中设置CheckBox
的Grid.Column
时,实际上只是向该CheckBox
的字典中添加一个条目。当Grid
需要知道CheckBox
所在的列时,会从该字典中查找对应的值。当将对象的Grid.IsSharedSizeScope
设置为True
时,该对象的字典将包含一个新属性 - 一个包含每个SharedSizeKey
的宽度/高度的字典。
我强调这是我的思维模型。我没有花时间用反编译工具查看Register
、GetValue
和SetValue
的实际实现方式以确定它们的确切工作方式。我可能对细节有所错误。但这是一个能够准确预测这些行为的模型,所以它足够好。
将属性值存储在字典中的概念对于C#程序员来说非常奇怪。然而这对于Python程序员来说已经司空见惯。在Python中,所有类属性 - 实际上是所有对象 - 都存储在字典中,因此您可以通过属性访问器或查找它们来获取它们的值。依赖属性和附加属性只是.NET从Java那里盗取了一切值得盗取的东西之后,正在从Python掠夺的另一种方式。(或者从Python掠夺的地方)学习Python使我成为了一个更好的C#程序员;我向还没有学过Python的任何C#开发人员推荐它。
依赖属性
示例,它创建了一个自定义控件文本框
,其中空格是不允许的,这意味着用户无法在文本框中输入空格。ValidatedTextBox
的类,并在该类文件中编写以下代码:public class ValidatedTextBox : TextBox
{
public ValidatedTextBox()
{
}
public static readonly DependencyProperty IsSpaceAllowedProperty =
DependencyProperty.Register("IsSpaceAllowed", typeof(bool), typeof(ValidatedTextBox));
public bool IsSpaceAllowed
{
get { return (bool)base.GetValue(IsSpaceAllowedProperty); }
set { base.SetValue(IsSpaceAllowedProperty, value); }
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (!IsSpaceAllowed && (e.Key == Key.Space))
{
e.Handled = true;
}
}
}
2) 现在将上述控件用于你的 .XAML
文件中
a) 添加自定义控件文本框的命名空间,如下所示:
xmlns:CustomControls="clr-namespace: ValidatedTextBox;assembly= ValidatedTextBox "
b) 现在,使用自定义控件文本框如下:
<CustomControls:ValidatedTextBox IsSpaceAllowed="False" x:Name="MyTextBox" />
它将创建一个自定义控件文本框,不允许空格。因此,基本上依赖属性允许添加功能,扩展任何控件的功能。
请看Josh Smith的这篇帖子,它包含了一些额外的信息。
http://joshsmithonwpf.wordpress.com/2007/06/22/overview-of-dependency-properties-in-wpf/