WPF:依赖属性与常规CLR属性的区别是什么?

Che*_*eso 6 wpf dependency-properties

在WPF中,成为"依赖属性"意味着什么呢?

我阅读了微软的依赖属性概述,但它并没有真正吸引我.部分文章说:

样式和模板是使用依赖项属性的两个主要激励方案.样式对于设置定义应用程序用户界面(UI)的属性特别有用.样式通常定义为XAML中的资源.样式与属性系统交互,因为它们通常包含特定属性的"setter",以及根据另一个属性的实时值更改属性值的"触发器".

然后示例代码是这样的:

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
....
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>
Run Code Online (Sandbox Code Playgroud)

但是我没有得到关于这一点的特别之处.这只是暗示,当我Style按下按钮到给定的样式时,我实际上是Background隐式设置的吗?这是它的关键吗?

Rob*_*ney 27

以下是依赖属性如何工作的解释,我总是希望有人为我写过.这是不完整的,也可能是错误的,但它将帮助您充分了解它们,以便您能够掌握所阅读的文档.

依赖属性是类似属性的值,可以通过DependencyObject类的方法获取和设置.它们可以(并且通常确实)看起来非常像CLR属性,但它们不是.这就是他们第一个令人困惑的事情.依赖属性实际上由几个组件组成.

这是一个例子:

DocumentRichTextBox对象的属性.这是一个真正的CLR属性.也就是说,它有一个名称,一个类型,一个getter和一个setter,就像任何其他CLR属性一样.但与"普通"属性不同,该RichTextBox属性不仅仅在实例中获取和设置私有值.在内部,它的实现方式如下:

public FlowDocument Document
{
   get { return (FlowDocument)GetValue(DocumentProperty); }
   set { SetValue(DocumentProperty, value); }
}
Run Code Online (Sandbox Code Playgroud)

当您设置Document的值你就会被传递到通过SetValue,沿DocumentProperty.什么是?如何GetValue获得它的价值?而且......为什么?

首先是什么.在RichTextBox命名上定义了一个静态属性DocumentProperty.声明此属性时,它是这样完成的:

public static DependencyProperty DocumentProperty = DependencyProperty.Register(
    "Document",
    typeof(FlowDocument), 
    typeof(RichTextBox));
Run Code Online (Sandbox Code Playgroud)

Register在这种情况下,该方法告诉依赖属性系统RichTextBox- 类型而不是实例 - 现在具有名为Documenttype 的依赖属性FlowDocument.此方法存储此信息......某处.确切地说,这是一个对我们隐藏的实现细节.

当为的setter Document属性调用SetValue,该SetValue方法着眼于DocumentProperty争论,验证它是真正属于一个性质RichTextBox,并且value是正确的类型,然后存储其新的价值...某处.DependencyObject这个实现细节的文档是腼腆的,因为你真的不需要知道它.在我的这个东西是如何工作的心理模型,我认为有类型的属性Dictionary<DependencyProperty, object>那是私有的DependencyObject,所以派生类(像RichTextBox),还看不出来,但GetValueSetValue可以对其进行更新.但谁知道呢,也许它是由僧侣写在羊皮纸上的.

无论如何,这个值现在被称为"本地值",也就是说它是一个特定的值,RichTextBox就像普通的属性一样.

所有一切的重点是:

  1. CLR代码不需要知道属性是依赖属性.它看起来与任何其他财产完全一样.您可以调用GetValueSetValue获取和设置它,但除非您使用依赖项属性系统执行某些操作,否则您可能不需要.
  2. 与普通属性不同,除了它所属的对象之外的其他东西可以参与获取和设置它.(可以想象,你可以通过反射做到这一点,但反思很慢.在字典中查找速度很快.)
  3. 这个东西 - 它是依赖属性系统 - 实际上位于对象及其依赖属性之间.它可以做各种各样的事情.

什么样的东西?好吧,让我们来看看一些用例.

捆绑. 绑定到属性时,它必须是依赖项属性.这是因为Binding对象实际上并没有在目标上设置属性,而是调用SetValue目标对象.

样式. 将对象的依赖项属性设置为新值时,SetValue告诉样式系统您已执行此操作.这就是触发器如何工作:它们没有发现属性的值已经通过魔法改变了,依赖属性系统告诉他们.

动态资源. 如果您编写类似XAML Background={DynamicResource MyBackground},则可以更改MyBackground资源的值,并更新引用它的对象的背景.这也不是魔术; 动态资源调用SetValue.

动画. 动画通过操纵属性值来工作.那些必须是依赖属性,因为动画正在调用SetValue它们.

更改通知. 注册依赖项属性时,还可以指定SetValue在设置属性值时调用的函数.

价值继承. 注册依赖项属性时,可以指定它参与属性值继承.当您调用GetValue以获取对象的依赖项属性的值时,GetValue查看是否存在本地值.如果没有,它会遍历父对象链,查看该属性本地值.

这就是你可以设置FontFamily一个Window神奇的东西(我经常使用那个词),窗口中的每个控件都使用新的字体.此外,它是如何在一个窗口中拥有数百个控件,而每个控件都没有FontFamily成员变量来跟踪它们的字体(因为它们没有本地值)但你仍然可以设置FontFamily任何一个控件(因为每个人DependencyObject拥有的值得隐藏的价值字典.


Rhy*_*hys 9

了解依赖属性试图解决的问题可能会有所帮助.

如果我们将Binding,Animation和Change Event模型放在一边,就像在其他答案中讨论过的那样,那么好处是内存使用,因此可以在窗口中托管数千个WPF对象.

如果窗口包含1000分Label的对象与每个Label具有通常的对象Foreground,Background,FontFamily,FontSize,FontWeight,等等,那么传统上这将消耗存储器,因为每个属性将具有私有支持字段,以存储该值.

大多数应用程序只会更改一些属性,其中大部分将保留其默认值.基本上非常浪费和冗余的信息(每个对象只在内存中保持相同的默认值)

这是依赖属性不同的地方.

// Lets register the Dependency Property with a default value of 20.5
public static readonly DependencyProperty ColumnWidthProperty =
    DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MyWPFControl), new UIPropertyMetadata(20.5, ColWitdhPropChanged));

public double ColumnWidth
{
  get { return (double)GetValue(ColumnWidthProperty); }
  set { SetValue(ColumnWidthProperty, value); }
}
Run Code Online (Sandbox Code Playgroud)

没有私人支持领域.注册依赖项属性时,可以指定默认值.因此,在大多数情况下,返回的值GetValue是默认值,该值仅存储一次,以覆盖Label应用程序所有窗口中对象的所有实例.

当使用SetValue它设置依赖项属性时,它将非默认值存储在由对象实例标识的集合中,以便在所有后续GetValue调用中返回.

因此,此存储方法仅消耗已从默认值更改的WPF对象的属性的内存.即仅与默认值的差异.


Ree*_*sey 8

在WPF中,成为"依赖属性"意味着什么呢?

为了成为依赖属性,该属性实际上必须在类上静态地定义为DependencyProperty.依赖属性系统与标准CLR属性非常不同.

但是,依赖属性的处理方式却截然不同.类型静态定义依赖项属性,并提供默认值.在需要之前,运行时实际上不会为实例生成值.这提供了一个好处 - 在请求类型之前该属性不存在,因此您可以拥有大量属性而无需开销.

这就是使样式工作属性的原因,但是通过可视树允许附加属性,属性"继承"以及WPF依赖的许多其他东西也很重要.

例如,获取DataContext依赖项属性.通常,您DataContext为Window或UserControl 设置依赖项属性.默认情况下,该窗口中的所有控件DataContext都会自动"继承"其父级的属性,这允许您为控件指定数据绑定.使用标准CLR属性,您需要为窗口中的每个控件定义DataContext,以使绑定正常工作.