我已经在几本书中读到了依赖属性,但是它们都有一个共同点,它们只是告诉我们它们是如何实现的(使用static readonly DependencyProperty
等),而没有告诉它们从内部工作的确切方式。
我的意思是它们被实现为静态,但仍然适用于所有对象。第二点困惑是附加属性。
有没有可用的教程可以轻松解释所有这些概念?
任何DependencyObject
类实现两个特殊属性。一个是类的静态属性,是DependencyProperty
对象的字典。该类的每个实例都可以在字典中查找有关每个DependencyProperty
的元信息-属性的名称,其类型,在获取和设置时必须调用的任何回调,它如何参与属性继承等等。注册依赖项属性时,您正在向此字典添加一个条目。
另一个属性是一个实例属性:这是一个由DependencyProperty
键控的字典,它包含每个DependencyProperty
的本地
value(如果已设置)。
在CLR属性的setter和getter中实现的[SetValue
和GetValue
方法基本上是对类固醇的惰性评估。他们没有在后备字段中存储和检索属性的值,而是在值字典中存储和检索属性的值。依赖属性的魔力在于GetValue
和SetValue
的实际作用。
GetValue
在对象的值字典中查找属性的值。如果找不到,它将在父元素上调用GetValue
,以获取父元素认为的值。例如,当您在TextBox
中创建Window
时,任何看TextBox
的FontFamily
的对象实际上都是在调用GetValue
。除非您明确设置字体,否则在其字典中没有该属性的条目。因此,GetValue
向父元素询问该值。父元素可以设置FontFamily
,也可以不设置。如果不是,则调用GetValue
的
its
以从its父级返回值。依此类推,直到到达Window
对象并找到实际的FontFamily
值。如果在FontFamily
上设置TextBox
,则SetValue
将值存储在值字典中。下次需要获取该FontFamily
的TextBox
值时,GetValue
在字典中找到该值并将其返回,因此不需要询问父元素。如果在FontFamily
上设置Window
,则SetValue
不仅会更新Window
的值字典中的值,还会触发一个属性更改事件,所有依赖于该属性的事件都会听到。 (记住,这就是为什么它们被称为依赖属性的原因。)如果依赖于该属性的事物本身是依赖属性,则它会触发自己的属性更改事件。就是这样,更改FontFamily
上的Window
会更改窗口中每个控件的字体,并提示WPF重新呈现已更改的控件。
附加属性使用相同的方法工作。任何可以具有附加属性的对象都具有一个字典,该属性存储了附加属性的值。在XAML中将Grid.Column
设置为CheckBox
时,您只是在该CheckBox
的字典中添加了一个条目。当Grid
需要知道CheckBox
在哪一列时,它将从该字典中查找值。当您在对象上将Grid.IsSharedSizeScope
设置为True
时,该对象的字典将包含一个新属性-字典,其中每个SharedSizeKey
都包含宽度/高度。
我应该强调,这是我的心理模型。我没有坐在Reflector上,而是研究了Register
,GetValue
和SetValue
的
actual
实现,以了解它们的实际工作方式。我可能在细节上是错的。但这是一个可以准确预测这些东西的行为的模型,因此足够好。在字典中存储属性值的概念对于C#程序员来说很奇怪。不过,这对Python程序员来说是一顶老帽子。在Python中,所有类属性(实际上是所有对象)都存储在词典中,因此您可以通过属性访问器或通过查找它们来获取它们的值。依赖属性和附加属性只是.NET窃取Java值得窃取的所有内容而如今掠夺Python的另一种方式。 (或者从Python掠夺的任何地方。)学习Python使我成为了一个更好的C#程序员。我将其推荐给尚未完成的任何C#开发人员。dependency property
的一个非常基本的示例,该示例创建了一个custom control text box
,其中不允许有空格,这意味着用户无法在文本框中键入空格。