【问题标题】:How Dependency Property tell the object to apply to?依赖属性如何告诉对象应用?
【发布时间】:2013-10-29 15:28:23
【问题描述】:

(我对这个概念完全陌生,所以我可能会问一些非常基本的问题。)

使用以下代码注册依赖属性:

public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata);

从逻辑上讲,它只是将属性名称与所有者类型相关联。

因此,如果我有多个所有者类型的实例,并且每个实例都将 DP 设置为不同的值。

如何存储这些值?

更新 1 - 2013 年 10 月 30 日上午 10:04

我从这里了解了附加属性:http://wpftutorial.net/DependencyProperties.html

附加属性

附加属性是一种特殊的 DependencyProperties。他们 允许您将值附加到不知道任何内容的对象 关于这个值。

布局面板就是这个概念的一个很好的例子。每个布局面板 需要不同的数据来对齐其子元素。帆布需要顶部 和左,DockPanel需要Dock等。既然可以自己写 布局面板,列表是无限的。所以你看,这是不可能的 在所有 WPF 控件上具有所有这些属性。

解决方案是附加属性。它们由控件定义 在特定上下文中需要来自另一个控件的数据。为了 例如由父布局面板对齐的元素。

所以在下面的代码sn-p中:

<Canvas>
    <Button Canvas.Top="20" Canvas.Left="20" Content="Click me!"/>
    <Button Canvas.Top="40" Canvas.Left="20" Content="Click me!"/>
</Canvas>

显然我们不能将 TopLeft 等所有对齐属性都赋予 Button。所以 Canvas 定义了这样的属性,它们“附加”到 Button 控件。

当 Canvas.Top 在 XAML 中被指定为 Button 的“属性”时,它将调用在 Canvas 类型中定义的 SetTop() 方法。并且 Button 作为元素参数传入。 我认为这就是 Canvas 知道哪个 Button 使用哪个 Top 值的方式。

public static void SetTop(UIElement element, double length);

但我不明白为什么附加属性必须是依赖属性?它们之间有什么联系?

谢谢!

【问题讨论】:

  • 这是由 DependencyObject 班级完成的。您不能在任何不是 DependencyObject 的对象上设置依赖属性。 DependencyObject 定义方法 GetValue 和 SetValue(以及其他一些方法)来获取/设置依赖属性值。这一切都在 MSDN 上的Dependency Properties Overview 文章中进行了解释。
  • 这是我读过的关于此类问题的最佳教程之一。和它简单的英语。 :) wpftutorial.net/DependencyProperties.html 你应该记住的是,你不必关心幕后发生的事情,你只需要知道全局定义的字典和散列集维护依赖属性。那些家伙太坏了,他们跑得非常快,允许属性继承、属性动画和依赖属性值优先级。 :) 顺便说一句,就像 Clemens 已经说过的那样,如果没有 DependencyObject,这个概念就行不通。
  • “但我不明白为什么附加属性必须是依赖属性?”。仅仅因为获取和设置附加属性的值也是由DependencyObject.GetValueDependencyObject.SetValue 完成的。 Canvas.SetTop(e, t) 只需调用e.SetValue(Canvas.TopProperty, t)
  • 这一切都在 MSDN 上的Attached Properties Overview 文章中进行了解释。
  • @smwikipedia - 附加属性是一个依赖属性,因为它还需要支持绑定、动画和属性元数据。这还不够公平吗?

标签: wpf dependency-properties


【解决方案1】:

通常当我们定义 DependencyProperty 时,我们还定义了一个 CLR“包装器”,它使我们能够在代码中使用 DependencyProperty

public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
    "Items", typeof(ObservableCollection<string>), typeof(MainWindow), 
     new UIPropertyMetadata(new ObservableCollection<string>()));

public ObservableCollection<string> Items
{
    get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
    set { SetValue(ItemsProperty, value); }
}

在这里您可以看到@Clemens 所谈论的GetValueSetValue 方法。我们可以在Window 和/或UserControl 中访问这些方法,因为它们都扩展了DependencyObject 类。还可以看到这里的Items属性不是static...只是DependencyProperty的定义就是static


更新>>>

为什么附加属性必须是DependencyProperty 因为在.NET 中,它们只是......它们就是这样设计的。一个更好的问题可能是,作为DependencyProperty 的附加属性有什么好处?

这个问题的答案就像被问到属性从DependencyProperty 获得什么好处?主要好处是这些属性可以在Bindings 中使用, Styles、Animations 和 Resources 等等。对于任何 WPF 开发人员,可以从 MSDN 上的两个非常重要的页面(已链接到 cmets)中找到更多详细信息:

Dependency Properties Overview

Attached Properties Overview

【讨论】:

  • 请注意,通过属性元数据为集合类型依赖属性设置默认值被认为是不好的做法。有关说明,请参阅 here
  • +1 @Clemens,非常感谢您的提示……非常有趣的东西。我总是热衷于寻找新的东西。我从来没有遇到过这个问题,因为我的大部分DependencyPropertys 都是在一次只使用一个实例的视图模型中定义的,但如果我遇到它,我会被难住的。我简化了我的代码示例。再次感谢。
  • @Sheridan 非常感谢。我已经更新了我的帖子,提出了更多问题。
  • @smwikipedia 与其“用更多问题更新你的帖子”,不如问一个新问题,因为这里已经回答了原来的问题。
【解决方案2】:

要回答你的问题,我需要从依赖系统的基础开始(你已经知道下面的大部分内容,但我认为阅读后你的问题可能会自己回答)

DependencyProperty 应该在派生自DependencyObject 的类中定义,因为DependencyObject 实现了一些方法和成员(其中大多数是私有的,开发人员看不到它们)。这些实现为DependencyObject 提供了使用对类的静态引用(而不是对类的实例的引用)来存储和检索数据的能力

我们存储和检索数据的方式是通过DependencyProperty 的包装器或直接通过GetValue 和SetValue(它们是DependencyObject 的方法)或通过绑定。无论哪种方式,静态DependencyProperty 都是找到正确值的“关键”。

当您注册 DependencyProperty 时,Dependency 系统会被告知新的 DependencyProperty 已添加到其资源中(类似于列表,其中填充了您已注册的 DependencyProperties)。现在,如果您将某些内容存储为[this.]SetValue(MyProperty, value),那么您实际上是在更新对象this(您可以将其想象为this.Resources[MyProperty])的资源。

这就像外部的普通属性一样,但考虑到UIPropertyMetadata 和其他Dependency 的东西,你的DependencyProperty 配备了一些独特的能力来强制、参与动画、更改时通知和等等,并且可以做的不仅仅是一个简单的属性。

还记得扩展名吗?

public static void MyExtension(this MyType target, string parameter, ...)
{
    target.MyFunction(parameter, ...);
}
var t = new MyType();
t.MyExtension("test");

AttachedPropertyDependencyProperty 和扩展名的组合,但有一些区别:

  1. AttachedProperty 的包装器是静态的,并且将类引用作为输入以了解它们的附加位置。 DependencyProperty 的目标通常是 this (除非您将 GetValue 和 SetValue 更改为 otherObject.SetValue(MyProperty, value) 之类的东西)。但与DependencyProperty 不同,AttachedProperty 的目标(本身必须是DependencyObject)作为参数传递给它。再说一遍,它必须是DependencyObject 的原因是只有DependencyObject 能够拥有一组资源来存储DependencyProperty

  2. 不像DependencyProperty使用RegisterAttachedProperty使用RegisterAttached方法告诉依赖系统其他DependencyObjects可以有这个属性,但实际的静态属性仍然存储在@987654354 @ 其中有AttachedProperty

  3. 在扩展中,this 在其第一个参数用于“扩展”它旁边的类型之前。在AttachedProperty 中没有this,实际上它始终是扩展的DependencyObject 类。

  4. AttachedProperty 不同,除非您编写几行类似于AttachedProperty 实现的代码,否则扩展无法自行存储数据。例如实现KeyValuePairs 的静态列表(实际数据的值和知道它们属于谁的键)


编辑:

在 Clemens 的评论之后,我需要补充一点,AttachedProperty 实际上并不是一个扩展。我提出扩展的原因是,通过比较这两者,您可以看到普通属性无法附加到 DependencyObject 的原因,因为为了将数据存储在其他对象中,需要像 Resource 这样的集合( DependencyObject 有)。

【讨论】:

  • 附加属性和扩展方法完全不相关,说“附加属性在某种程度上是依赖属性和扩展(方法)的组合”是错误的。附加属性的 CLR 包装器是静态方法这一简单事实绝不会创建与扩展方法的关系。
  • 扩展是简单的静态方法,具有一些附加行为,就像附加属性一样。它们通过为它没有的对象启用某些东西来彼此相似。区别在于依赖对象类的实现。
  • “还有一些额外的行为,就像附加的属性一样”。这是模糊和令人困惑的,充其量表明您还没有完全理解这两个不同的概念。附加属性和扩展方法的 CLR 包装器的唯一共同点是它们是静态方法。就是这样。
  • 我认为你错过了 Clemens 的重点,我试图帮助 'smwikipedia' 理解为什么附加属性必须是依赖属性以及为什么它不能是普通属性,因为它看起来像扩展。我并不是说它们是相同的,但是可以使用扩展和一些代码来实现非依赖附加属性。
  • 但你是对的,这是误导性的,感谢你的评论,我已经编辑了我的答案。
【解决方案3】:

我一直将DependencyProperties 视为属性定义,与普通属性不同。

属性定义包含属性类型、名称、默认值等,并包含一个指示在何处查找属性值的指针。但它不包含值本身。

这允许您在绑定中使用DependencyProperties,因为您可以将属性设置为从绑定表达式而不是对象本身获取其值。

这对AttachedProperties 很重要,因为它们的值并不存在于它们所附加的对象上。他们需要能够在与对象本身不同的位置查找其值,DependencyProperties 允许这样做。

您不能对常规属性执行此操作,因为常规属性的值应在对象本身上找到,但AttachedProperty 不存在于它所附加到的对象上。

这是简单的版本。 :)

【讨论】:

    猜你喜欢
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 2012-09-12
    • 2017-07-03
    • 2021-11-29
    • 2016-08-28
    • 2018-03-06
    • 1970-01-01
    相关资源
    最近更新 更多