【发布时间】:2011-05-19 19:53:16
【问题描述】:
我在 SO 和其他网站上阅读了很多关于依赖属性的信息。但是,真的没有找到一个很好的解释,仍然很困惑。我同时使用 SL 和 WPF。就实现而言,它们在 SL 和 WPF 中是否有所不同?为什么我们真的需要它们?它们是静态的是否意味着它们的价值观是共享的? MS 引入依赖属性的原因是什么?
【问题讨论】:
标签: .net wpf silverlight dependency-properties
我在 SO 和其他网站上阅读了很多关于依赖属性的信息。但是,真的没有找到一个很好的解释,仍然很困惑。我同时使用 SL 和 WPF。就实现而言,它们在 SL 和 WPF 中是否有所不同?为什么我们真的需要它们?它们是静态的是否意味着它们的价值观是共享的? MS 引入依赖属性的原因是什么?
【问题讨论】:
标签: .net wpf silverlight dependency-properties
查看 MSDN 上的 Dependency Properties Overview(或 Silverlight version)。
(Silverlight/WPF) 提供了一组服务,可用于扩展 CLR 属性的功能。这些服务统称为(Silverlight/WPF)属性系统。由 (Silverlight/WPF) 属性系统支持的属性称为依赖属性。
有关 Silverlight 和 WPF 属性系统之间的差异,请参阅here。
依赖属性系统以及将属性与 Silverlight/WPF 类型系统连接起来很重要的几个原因如下(请参阅here 以获得更详细的解释):
请注意,依赖属性通常由 CLR 属性支持,这意味着您可以在代码中与它进行交互,就像使用 CLR 属性一样。
【讨论】:
它们在 SL 和 WPF 中是否不同?
否
为什么我们真的需要它们?
简短的回答是,我们需要它们来进行数据绑定和样式设置,这就是 MS 添加它们的原因。
它们是静态的意味着它们的值是共享的吗?
静态部分是属性的名称,因此可以使用 GetValue(propName) 检索并使用 SetValue(propName,值)
【讨论】:
其他人已经提供了依赖属性的解释。我将尝试围绕他们的设计提供一些背景信息。 (它帮助我理解了为什么这些被称为依赖属性的奇怪创造首先存在。)WPF 的设计者添加依赖属性而不是使用普通属性有几个原因......
如果微软提供一种更好的语法来创建新的依赖属性,通过使用编译器魔法隐藏重复的代码,就像他们为迭代器所做的那样,那就太好了。也许在 C# 6 中... :)
【讨论】:
您提出的大多数问题都已得到其他人的回答,但我会简单介绍一下。
首先,它们在 Silverlight 和 WPF 之间有所不同,WPF 依赖属性具有更多功能。但是,除了 WPF 中的这些附加功能外,它们基本相同。
最大的问题是您应该“何时”使用它们(“为什么”大部分已经得到解答)。当您需要在 XAML 中为它们分配标记扩展(例如绑定表达式)时,您基本上需要它们,然后将在运行时解析/评估。大多数情况下,只有在编写公开属性的自定义控件时才需要这样做。
即您将自定义控件拖放到 XAML 中的表面上,并将绑定表达式分配给其属性之一。
我看到有些人到处使用它们,认为在进行 Silverlight/WPF 开发时需要它们,但事实并非如此。不同的人在沙地上画出不同的线条,但我说只在必要时使用它们——这几乎 100% 的时间都在自定义控件中。
明显的自我推销:我在我的书 Pro Business Applications in Silverlight 4 的第 10 章对此主题进行了更深入的(特定于 Silverlight)的讨论。
【讨论】:
WPF 是围绕基于属性的架构设计的,它需要一个非常强大的属性系统支持 -
我发现非常有用的文章 -
中的依赖属性概述 WPF: http://joshsmithonwpf.wordpress.com/2007/06/22/overview-of-dependency-properties-in-wpf/
内部依赖属性: http://www.i-programmer.info/programming/wpf-workings/443-inside-dependency-properties-.html
WPF:依赖的静态本质 特性: http://dotnetslackers.com/Debugger/re-126399_WPF_The_Static_Nature_of_Dependency_Properties.aspx
【讨论】:
添加到其他答案中已经涵盖的内容:
从概念上讲,依赖属性的核心是一个系统,允许查询基于不同来源的列表获取值,而不是返回单个存储值。正如许多其他答案中列出的那样,这有很多后果,但实际发生的事情会导致在获取值时表现出这样的行为(当然是大大简化了):
private string _coerced;
private string _animated;
private string _local;
private string _triggered;
private string _styled;
private string _inherited;
private string _default;
public string MyDP
{
get
{
return _coerced ?? _animated ?? _local ?? _triggered ?? _styled ?? _inherited ?? _default;
}
}
【讨论】:
答案就在名称本身,尽管“依赖”这个词在软件开发中充满了意义,以至于它的含义并不是特别清楚。
依赖属性是一个对象的属性,它的值依赖在某个other对象上。所以,例如:
TextBox 的FontFamily 属性的值可以(并且通常确实)取决于其容器的FontFamily 属性。如果更改容器上的属性,TextBox 上的值会更改。
TextBox 的 Text 属性的值可能取决于绑定的数据源。当绑定属性的值发生变化时,Text 属性的值也会发生变化。
Label 的 Opacity 属性的值可能取决于动画情节提要,在常见情况下,您已设置 UI 元素以淡入或淡出以响应某些事件.
任何 UI 元素上各种属性的值都取决于您应用到它们的样式。
依赖背后的核心概念是,所依赖的事物应该从它所依赖的事物中获取属性值。这就是为什么依赖属性被实现为一个 CLR 属性,它的 getter 调用一个方法。
当TextBox 或渲染它的东西需要知道它的FontFamily 是什么时,FontFamily getter 调用GetValue 方法。该方法从容器、动画、绑定等中查找值。
这种方法非常复杂。例如,如果值是继承的,它的工作方式与 WPF 在资源字典中查找样式的方式非常相似:它在本地字典中查找值,如果没有条目,则在其父字典中查找,并且一直如此,直到找到一个值或到达层次结构的顶部,在这种情况下,它使用默认值。
如果您查看依赖属性的实现,您就会发现。依赖对象有一个字典,它可能包含也可能不包含给定属性的条目。 GetValue 方法从该字典中获取值(这就是具有依赖属性的对象可以具有覆盖它们所继承的本地值的方式),如果它没有找到该值,它使用关于属性的元信息来弄清楚它应该在哪里看。
由于该类中的每个对象的元信息都是相同的(即,TextBox.Text 对每个 TextBox 的作用相同),因此存储它的字典是该类的静态属性。
所以当你看到这样的代码时:
static Button()
{
// Register the property
Button.IsDefaultProperty =
DependencyProperty.Register("IsDefault",
typeof(bool), typeof(Button),
new FrameworkPropertyMetadata(false,
new PropertyChangedCallback(OnIsDefaultChanged)));
}
正在发生的事情是,定义所有 Button 对象上的 IsDefault 属性的元信息被添加到该字典中。当你看到这个时:
public bool IsDefault
{
get { return (bool)GetValue(Button.IsDefaultProperty); }
set { SetValue(Button.IsDefaultProperty, value); }
}
您看到的是基于元信息查找属性值(从本地字典、父对象或其他)的 getter 方法。
还记得我说过 getter 查找属性值的第一个位置是对象的本地字典吗? setter 中的SetValue 方法是如何将该条目添加到字典中(如果曾经调用过,只有当您通过显式设置属性来覆盖依赖项时才会出现这种情况,即说“我想要这个TextBox在Consolas 中显示文本,而不管窗口中的其他控件正在使用什么。")。
我们从这种明显复杂的系统中获得的一个非常重要的好处是,对象的依赖属性只有在设置时才会消耗内存。如果我创建 10,000 个TextBox 控件并将它们添加到Window,则其中没有一个实际上包含对FontFamily 对象的引用。那是 10,000 个对象引用,我没有为其分配内存,垃圾收集器也没有检查。事实上,如果 TextBox 有 100 个依赖项属性(它确实有),那么无论何时创建一个 TextBox,这就是 100 个支持字段,您不会为其分配内存。依赖属性仅在显式设置时才会消耗内存。由于 UI 对象上的绝大多数属性永远不会被显式设置,因此可以节省大量资金。
【讨论】: