【问题标题】:Difference between Attached and non-Attached Dependency Properties in SilverlightSilverlight 中附加和非附加依赖属性之间的区别
【发布时间】:2011-07-27 11:56:26
【问题描述】:

好的 Stackers,我在这个问题上花了好几个小时,我想知道是否有人有明确的答案。
对于我所做的所有研究,我在 Silverlight 中找不到 .Register.RegisterAttached 之间的任何区别。现在,在你开枪告诉我.RegisterAttached 用于将DP 附加到另一个类之前,请尝试使用DependencyProperty.Register() 实现附加依赖属性。我没有发现任何区别,所以我不知道区别是什么。
此外,在我的具体情况下,我正在尝试扩展 Grid 类的功能,并希望给它一些额外的属性。因此,我尝试列出将 typeof(Grid)typeof(FluidLayoutManager) (实现类)作为 ownerType 参数传递,它似乎也没有什么区别......(我相信当我通过两个来自同一命名空间的自定义类。但是,当传递 Microsoft 定义的类与自定义类时,我总是让它在 XAML 中显示为自定义类的 DP。)
对此主题的任何澄清将不胜感激,因为我坐在这里摸不着头脑,想知道是否有任何区别,或者微软是否又在和我开玩笑。

【问题讨论】:

    标签: c# silverlight dependency-properties attached-properties


    【解决方案1】:

    考虑到在 cmets 中的讨论,我将尝试用简单的英语来做这个:

    附加的依赖属性和依赖属性之间的主要区别(以及 .Register 和 .RegisterAttached 之间)是 RegisterAttached 允许将值分配给任何依赖对象,而 Register 只允许将其附加到作为传递的类ownerType 参数。

    正如Haris Hasan 提到的(在评论线程的深处),您的示例使用了唯一允许的类型(即 CustomControl),并且没有向您显示可以将附加版本分配给任何依赖项对象。

    例如您可以使用附加的依赖属性(但不是普通的 DP)来做到这一点:

    <Grid local:AttacherClass.ADP1="1" x:Name="LayoutRoot" Background="White">
    </Grid>
    

    我能找到的关于 ADP 的最佳参考是这个:http://msdn.microsoft.com/en-us/library/ms749011.aspx

    我们使用 ADP 作为本地化系统的基础,因此可以在加载过程中将翻译寄生到对象上,而不是使用非常长的绑定。 DP 无法做到这一点

    更新:

    我还想澄清一下,父限制适用于基于 XAML 的属性使用。从代码来看,父限制显然不适用。

    【讨论】:

    • 更多 XAML 信息 1:XAML 使用 SetDP/ADP()GetDP/ADP() 函数,但调用 SetValue(DP/ADP, value)GetValue(DP/ADP) 不使用。 2: 在 XAML 设计器中,Intellisense 将使用SetDP/ADP()GetDP/ADP() 方法签名来确定是显示依赖属性还是附加依赖属性。因此,如果SetDP/ADP() 参数接受DependencyObject 而不是更具体的类,它将显示无效的依赖属性。
    • +1 评论...现在我只需要将 that 也翻译成清晰的英文,我们就完成了 :)
    • 所以我的下一个问题是,两者之间是否还有其他区别,.RegisterAttached 中传递的 ownerType 参数在 Silverlight 中是否真的意味着什么?跨度>
    • @Melodatron:问得好。我似乎记得一些关于附加属性与特定类型的父级相关的内容(它被称为 Owner Type),但我看不出它实际上是如何在任何地方使用来限制的它。它实际上看起来更像是暗示关系而不是强制它。
    【解决方案2】:

    认为“RegisterAttached 允许将值分配给任何依赖对象,而 Register 只允许将其附加到作为 ownerType 参数传递的类”是错误的。这是一个在 Register 上注册的附加属性的完美工作示例:

    class FooPropertyDeclaringType
    {
        public static readonly DependencyProperty FooProperty = 
            DependencyProperty.Register("Foo", typeof(int), typeof(FooPropertyDeclaringType));
    }
    
    class SomeUnrelatedType : DependencyObject { }
    
    class Program
    {
        static void Main()
        {
            var obj = new SomeUnrelatedType();
            obj.SetValue(FooPropertyDeclaringType.FooProperty, 10);
            Debug.Assert(10 == (int)obj.GetValue(FooPropertyDeclaringType.FooProperty));
        }
    }
    

    Reflector 表明 Register 和 RegisterAttached 之间的唯一区别是 Register 抛出了大部分提供的元数据,并且只为注册类的实例保留它(通过 OverrideMetadata)。这意味着通常在元数据中指定的 Inherits 和各种更新通知等属性不适用于通过 Register 注册并附加到其他类型(注册类型除外)对象的属性。所以Register实际上是RegisterAttached的精简版。这样做可能是出于性能原因。

    在由 Haris Hasan 在 cmets 中链接到他的答案的 example 中,如果您将 RegisterAttached 更改为 Register,按钮将停止移动(因为该属性不再为 Button 类型提供 AffectsParentArrange 元数据),但它们仍然在它们的调整窗口大小时的新位置。但是,如果在调用 InitializeComponent() 之后将相同的元数据添加到 Button 类型:

    RadialPanel.AngleProperty.OverrideMetadata(
        typeof(Button), 
        new FrameworkPropertyMetadata(
            0.0, FrameworkPropertyMetadataOptions.AffectsParentArrange));
    

    然后一切都恢复正常,就像调用了 RegisterAttached 一样。

    【讨论】:

    • 澄清一下,我提到的父限制适用于在 XAML 中使用属性,而不是在代码中。
    【解决方案3】:

    就实现而言,它们可能没有太大区别,但它们在操作上有所不同,即它们的作用和用途不同。

    Simple Register 用于简单的依赖属性,通常用于绑定和验证,因此它们是普通的 CLR 属性,具有一些有助于 WPF 的附加魔法

    RegisterAttached 通常用于您想要公开可以在子类中访问和设置的属性,例如DockPanel,其中控制的子级使用Dock.LeftDock.Right 告诉父级他们想要放置的位置.所以它们是一种特殊的依赖属性,可以在子控件中访问(简单的Register 属性不是这种情况)并且它们(在DockPanel 的情况下)帮助父控件显示子控件

    简而言之,可以说Register 用于注册dependency properties,它们在同一类中使用,而RegisterAttached 用于注册称为attached properties 的特殊依赖属性,它们被除一类以外的类使用和访问定义它

    This 很好地解释了 Attached Properties 以及通过简单的 DP 无法实现的功能

    【讨论】:

    • 另外一个注意事项(可以派上用场):在解析包含属性的 Xaml 时会调用附加属性设置器。依赖设置器仅由您调用(其余时间属性仅由 SetValue 和 GetValue 访问。
    • @Haris:请考虑对我提出的问题的修改。我了解“Register/RegisterAttached 通常用于...”,但这在实现或功能方面并没有什么不同,这是我想要回答的问题。此外,正如您在示例中看到的那样,从另一个类访问“已注册”DP 绝对没有问题。
    • @Melodatron:问题是您试图在错误的上下文中使用示例中的Attached Property。您正在使用Attached Property,即使是简单的dependency property 也可以使用。正如我所说的Attached Property 是一个具有附加功能的dependency property,这就是为什么您能够以同样的方式访问它的原因。但是在您的示例中,您没有使用Attached Properties 的实际功能。您不是从另一个类访问它,这是它与简单的dependency property 的主要区别。
    • @Haris:我接受你的说法,ADP 是扩展的 DP,但是我要说的是,对我来说,目前,这些“附加功能”领域完全是个谜我。如果您可以概述和/或举例说明 ADP 可以做而 DP 做不到的事情,我将不胜感激。
    • 我现在没有时间做一个例子,但互联网上到处都是这些。例如,请参阅blogs.msdn.com/b/bencon/archive/2006/07/24/677520.aspx。你无法通过普通的 DP 做到这一点,而这正是 ADP 真正派上用场并展示其力量的地方
    【解决方案4】:

    如果您向 RegisterAttached 注册,它会成为任何 DependencyObject 存储中的全局属性,即您可以在任何 DependencyObject 上设置值

    如果在调用 Get/Setvalue 时使用 Register,则会检查该调用是否是可转换为注册类型的对象。

    类似于 RegisterAttached 的属性示例是 Grid.Row 和 Grid.Column。

    【讨论】:

    • 好的,所以虽然我可以为阅读“一个行为类似于 RegisterAttached 的属性的示例是 Grid.Row 和 Grid.Column.”而尖叫,但实际上你有一个观点.到目前为止,我发现的唯一区别是使用 IncompatibleControl.SetValue() 时,我能够在 C# 中设置和检索 Attached Dependency 值。但是,DP 会引发错误,并且在 XAML 中都不起作用。
    【解决方案5】:

    那么 RegisterAttached 中的 'ownerType' 到底是做什么用的? 这个问题一直困扰着我好几年,所以我终于仔细研究了 WindowsBase 中的 4.6.1 代码。

    对于任何DependencyProperty,无论是否附加,最终归结为PropertyDescriptor WPF 为后期绑定XAML 访问获得什么类型,直到第一次才确定(在每个类型/property pairing based) 尝试进行此类访问。这种延迟是必要的,因为PropertyDescriptor 封装了绑定到特定类型的属性,而附加属性的目的是避免这种情况。

    到发生 XAML 访问时,Register(...)RegisterAttached(...) 的区别已经消失在(运行)时间的迷雾中。正如其他人在此页面上所指出的那样,“DependencyProperty”本身并没有编码附加与非变体之间的区别。假设每个 DP 都符合任一使用条件,仅受运行时可以计算出的内容的限制。

    例如,下面的 .NET 代码似乎依赖 not 在 'tOwner' 类型上找到匹配的 instance 属性 作为允许附加访问的第一个要求。为了确认这一诊断,它会检查“tOwner”是否公开了其中一种静态访问方法。这是一个模糊的检查,因为它不验证方法签名。这些签名只对 XAML 访问很重要;附加属性的所有实际运行时目标必须是DependencyObjects,WPF 会尽可能通过DependencyObject.GetValue/SetValue 访问它。 (据报道,VS 设计器确实使用了静态访问器,如果没有它们,您的 XAML 将无法编译)

    相关的.NET代码是静态函数DependencyPropertyDescriptor.FromProperty,这里用我自己的cmets展示(总结如下):

    internal static DependencyPropertyDescriptor FromProperty(DependencyProperty dp, Type tOwner, Type tTarget, bool _)
    {
        /// 1. 'tOwner' must define a true CLR property, as obtained via reflection, 
        /// in order to obtain a normal (i.e. non-attached) DependencyProperty
        if (tOwner.GetProperty(dp.Name) != null)
        {
            DependencyPropertyDescriptor dpd;
    
            var dict = descriptor_cache;
            lock (dict)
                if (dict.TryGetValue(dp, out dpd))
                    return dpd;
    
            dpd = new DependencyPropertyDescriptor(null, dp.Name, tTarget, dp, false);
            lock (dict)
                dict[dp] = dpd;
    
            /// 2. Exiting here means that, if instance properties are defined on tOwner,
            /// you will *never* get the attached property descriptor. Furthermore,
            /// static Get/Set accessors, if any, will be ignored in favor of those instance
            /// accessors, even when calling 'RegisterAttached'
            return dpd;
        }
    
        /// 3. To obtain an attached DependencyProperty, 'tOwner' must define a public,
        /// static 'get' or 'set' accessor (or both).
    
        if ((tOwner.GetMethod("Get" + dp.Name) == null) && (tOwner.GetMethod("Set" + dp.Name) == null))
            return null;
    
        /// 4. If we are able to get a descriptor for the attached property, it is a
        /// DependencyObjectPropertyDescriptor. This type and DependencyPropertyDescriptor
        /// both derive directly from ComponentModel.PropertyDescriptor so they share
        /// no 'is-a' relation.
    
        var dopd = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, tTarget);
        /// 5. Note: If the this line returns null, FromProperty isn't called below (new C# syntax)
    
        /// 6. FromProperty() uses the distinction between descriptor types mentioned in (4.)
        /// to configure 'IsAttached' on the returned DependencyProperty, so success here is 
        /// the only way attached property operations can succeed.
        return dopd?.FromProperty(dopd);
    }
    

    总结:当调用RegisterAttached 来创建附加的DependencyProperty 时,'ownerType' 的唯一用途是识别定义适当静态 Get/Set 访问器的类型。这些访问器中至少有一个必须存在于“ownerType”上,否则 XAML 附加访问将不会编译或静默失败。虽然 RegisterAttached 在这种情况下没有失败,而是成功返回了一个失效的 DP。对于仅用于附加使用的 DP,“tOwner”不必从 DependencyObject 派生。您可以使用任何常规的 .NET 类,无论是静态的还是非静态的,实际上甚至可以是结构体!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-25
      相关资源
      最近更新 更多