揭开依赖属性的神秘面纱

时间:2010-12-03 15:41:56

标签: .net wpf silverlight dependency-properties

我在SO和其他网站上已经阅读了很多依赖属性。但是,真的没有找到一个很好的解释,我仍然感到困惑。我同时使用SL和WPF。在实施方面,它们在SL和WPF方面有所不同吗?为什么我们真的需要它们?它们是否静态意味着它们的价值是共享的?为什么MS引入依赖属性的原因?

奖励: 我正在寻找一个更彻底,更完整的答案。

7 个答案:

答案 0 :(得分:19)

答案在于名称本身,虽然“依赖”这个词在软件开发中如此充满意义,但这并不是特别清楚这意味着什么。

依赖项属性是一个对象上的属性,其值依赖在某些其他对象上。所以,例如:

  • FontFamily的{​​{1}}属性的值可以(通常也是如此)取决于其容器的TextBox属性。如果更改容器上的属性,则FontFamily上的值会更改。

  • TextBox的{​​{1}}属性的值可能取决于绑定的数据源。当绑定属性的值更改时,Text属性的值将更改。

  • TextBox的{​​{1}}属性的值可能取决于动画故事板,在常见的场景中,您已设置要淡入或淡出的UI元素回应某些事件。

  • 任何UI元素上的各种属性的值可能取决于您应用于它们的样式。

依赖性背后的核心概念是,依赖的东西应该从它所依赖的东西中获取属性值。这就是为什么依赖属性被实现为其getter调用方法的CLR属性。

Text或呈现它的内容需要知道其Opacity是什么时,Label getter会调用TextBox方法。该方法从容器,动画或绑定或其他任何内容中查找值。

该方法有很多复杂性。例如,如果值继承,它的工作方式非常类似于WPF在资源字典中查找样式的方式:它在本地字典中查找以查找值,如果没有条目则在其父字典中查找,并且所以一直到它找到一个值或到达层次结构的顶部,在这种情况下它使用默认值。

如果你看一下依赖属性的实现,那就是你会发现的。依赖项对象具有可能包含或不包含给定属性的条目的字典。 FontFamily方法从该字典获取值(这是具有依赖项属性的对象可以具有覆盖它们继承的本地值的方式),如果它没有找到该值,则它使用关于该字典的元信息。找出它应该看的位置。

由于该元信息对于类中的每个对象都是相同的(即,FontFamily对于每个GetValue都是相同的),它存储的字典是该类的静态属性。

所以当你看到这样的代码时:

GetValue

正在发生的事情是,在所有TextBox.Text对象上定义TextBox属性的元信息正被添加到该字典中。当你看到这个:

static Button()
{
   // Register the property
   Button.IsDefaultProperty = 
     DependencyProperty.Register("IsDefault",
     typeof(bool), typeof(Button),
     new FrameworkPropertyMetadata(false,
        new PropertyChangedCallback(OnIsDefaultChanged)));
}

你所看到的是getter方法,它根据该元信息查找属性的值(来自本地字典,父对象或其他)。

还记得我是怎么说getter看起来找到属性值的第一个位置是对象的本地字典吗? setter中的IsDefault方法是将该条目添加到字典中的方式(如果它曾被调用过,只有当你通过显式设置属性来覆盖依赖项时才会这样,即说“我想要这个{ {1}}显示Button中的文字,无论窗口中的其他控件使用的是什么。“)。

我们从这种看似复杂的系统中获得的一个非常重要的好处是,对象的依赖属性只有在设置时才会消耗内存。如果我创建10,000个public bool IsDefault { get { return (bool)GetValue(Button.IsDefaultProperty); } set { SetValue(Button.IsDefaultProperty, value); } } 控件并将其添加到SetValue,则其中没有一个实际包含对TextBox对象的引用。这是10,000个对象引用,我没有为其分配内存,并且垃圾收集器没有检查。事实上,如果一个Consolas有100个依赖属性(并且确实如此),那么每当你创建一个TextBox时,那就是你没有为其分配内存的100个支持字段。如果您明确设置它们,依赖项属性仅消耗内存。由于UI对象上绝大多数属性都没有明确设置,因此可以节省大量资金。

答案 1 :(得分:11)

其他人提供了依赖属性的解释。我将尝试提供有关其设计的一些背景信息。 (它帮助我理解为什么这些奇怪的创作称为依赖属性首先存在。)WPF的设计者添加依赖属性而不是使用普通属性有几个原因......

  1. 能够为不适用于当前元素的属性添加值,但适用于其子元素。例如,在容器上设置字体并将其级联到任何包含的文本元素。
  2. 未使用属性的大小成本。以上面的字体为例......在设计良好的页面上,只有少数元素会修改其字体 - 可能是一些顶级容器。如果我们将Font存储为常规C#属性,则每个对象都需要一个32/64位指向字体对象的指针,其中大多数为null或具有默认值。通过XAML属性的数量和页面上的典型元素数量来放大这一点,最终需要大量空间来存储任何内容或默认值。将此与依赖项属性进行比较,其中值仅在设置时占用空间。 (我没有看过内部实现,但我想GetValue(propName)和SetValue(propName)将值存储在某种per-object / per-propety哈希表中。)
  3. 如果微软通过使用编译器魔术隐藏重复代码来提供创建新依赖项属性的更好语法,那将会很不错,就像它们对迭代器一样。也许在C#6 ...... :)中

答案 2 :(得分:4)

  1. SL和WPF有不同吗?

  2. 为什么我们真的需要它们?
    简短的回答是,我们需要它们用于数据绑定和样式化,这就是MS添加它们的原因。

  3. 它们是否静态意味着它们的价值是共享的? 静态部分是属性的名称,因此可以使用GetValue(propName)检索它并使用SetValue(propName,value)设置

答案 3 :(得分:3)

查看MSDN上的Dependency Properties Overview(或Silverlight version)。

  

(Silverlight / WPF)提供了一组可用于扩展CLR属性功能的服务。总的来说,这些服务通常被称为(Silverlight / WPF)属性系统。由(Silverlight / WPF)属性系统支持的属性称为依赖属性。

有关Silverlight和WPF属性系统之间的差异,请参阅here

Dependency Property系统以及将属性与Silverlight / WPF类型系统连接起来的重要原因如下(有关详细说明,请参阅here):

  • 允许以样式设置属性
  • 允许属性支持数据绑定
  • 允许属性设置动画
  • 允许自定义控件的属性在Visual Studio中接收设计器支持

请注意,Dependency属性通常由CLR属性支持,这意味着您可以使用与CLR属性相同的方式在代码中与其进行交互。

答案 4 :(得分:3)

WPF是围绕基于属性的架构设计的,它需要一个非常强大的属性系统支持 -

  1. 各种提供商的价值,即其价值可以在运行时由各种提供商改变,如触发器,样式,动画,主题等。(依赖属性值优先)
  2. XAML(表达式支持)。
  3. 内存效率(静态性)。
  4. 更改通知
  5. 属性值继承(附加属性)。
  6. 我发现非常有用的文章 -

      

    依赖属性概述   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

答案 5 :(得分:2)

你提出的大部分问题都得到了其他人的回答,但我会简单地给你一些看法。

首先,他们 在Silverlight和WPF之间不同,WPF依赖属性具有更多功能。但是,在WPF中排除这些附加功能,它们基本上是相同的。

最重要的问题是“何时”你应该使用它们(“为什么”大部分已经得到回答)。当您需要在XAML中为它们分配标记扩展(例如绑定表达式)时,您基本上需要它们,然后在运行时解析/评估它们。在大多数情况下,只有在编写公开属性的自定义控件时才需要这样做。

即。您在XAML中的表面上放置自定义控件,并将绑定表达式分配给其中一个属性。

我看到有些人到处使用它们,认为在进行Silverlight / WPF开发时需要它们,但事实并非如此。不同的人有不同的线条,他们在沙子中绘制,但我说只在必要时使用它们 - 几乎100%的时间都在自定义控件中。

Blantant self promotion:我在本书Pro Business Applications in Silverlight 4的第10章中对此主题进行了更深入的讨论(Silverlight)。

答案 6 :(得分:1)

添加其他答案中已涵盖的内容:

从概念上讲,它的核心依赖属性是一个允许查询根据不同来源列表获取值而不是返回单个存储值的系统。许多其他答案中列出了许多后果,但实际上发生的事情会导致在获得值时表现得像这样的事情(当然大规模简化):

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;
    }
}