Silverlight中附加和非附加依赖属性的区别

本文关键字:属性 区别 依赖 Silverlight | 更新日期: 2023-09-27 18:03:46

好了,我花了好几个小时在这个问题上,我想知道是否有人有一个明确的答案。
对于我所做的所有研究,我在Silverlight中找不到.Register.RegisterAttached之间的任何差异。现在,在您跳枪并告诉我.RegisterAttached用于将DP附加到另一个类之前,请尝试使用DependencyProperty.Register()实现附加依赖属性。我没有发现任何不同,所以我不知道有什么不同。
此外,在我的特定情况下,我试图扩展Grid类的功能,并希望给它一些额外的属性。因此,我试着将typeof(Grid)typeof(FluidLayoutManager)(实现类)作为ownerType参数传递给清单,它似乎也没有什么区别……(我相信当我从同一个名称空间传递两个自定义类时确实会产生差异。然而,当传递微软定义的类与自定义类时,我总是让它在XAML中显示为自定义类的DP。
任何关于这个话题的澄清将是非常感激的,因为我坐在这里挠头,想知道是否有任何不同,或者微软只是再一次欺骗我。

Silverlight中附加和非附加依赖属性的区别

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

附属依赖属性和依赖属性之间的主要区别(因此在.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作为本地化系统的基础,这样翻译可以在加载期间寄生到对象上,而不是使用可怕的长绑定。而DPs就不能这样

更新:

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

认为"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的简化版本。这样做可能是出于性能方面的考虑。

在哈里斯·哈桑在评论他的回答链接的例子中,如果你改变RegisterAttached到Register,按钮停止移动(因为属性不再为按钮类型提供AffectsParentArrange元数据),但是当你调整窗口大小时,它们仍然在新的位置重新绘制。但是,如果您在调用InitializeComponent()之后向Button类型添加相同的元数据:

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

然后一切又工作了,如果RegisterAttached被调用

就实现而言,它们可能没有太大的不同,但它们在动作上是不同的,即它们在做什么和用于什么方面是不同的。

Simple Register用于简单的依赖属性,这些属性通常用于绑定和验证,因此它们是普通的CLR属性,带有一些额外的魔法,可以帮助WPF

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

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

这是附加属性的一个很好的解释,什么不能通过简单的DP

如果你注册了RegisterAttached,它将成为任何DependencyObject存储中的全局属性,即你可以在任何依赖对象上SetValue

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

一个类似RegisterAttached属性的例子是Grid。行,网格,列。

那么究竟什么是'ownerType'用于RegisterAttached?这个问题已经困扰我好几年了,所以我终于仔细研究了一下WindowsBase的4.6.1代码。

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

当XAML访问发生时,Register(...)RegisterAttached(...)的区别已经消失在(运行)时间的迷雾中。正如其他人在本页所指出的,"DependencyProperty"本身并没有编码附加与非品种之间的区别。每个DP都被假定为符合任何一种用法,仅受运行时可以计算出的内容的限制。 例如,下面的。net代码似乎依赖于而不是在'tOwner'类型上找到匹配的实例属性作为允许附加访问的第一个要求。为了确认这个诊断,它然后检查'tOwner'是否暴露了一个静态访问方法。这是一种模糊的检查,因为它不验证方法签名。这些签名只对XAML访问有影响;附加属性的所有实际运行时目标必须是DependencyObject s, WPF尽可能通过DependencyObject.GetValue/SetValue访问它。(据报道VS设计器确实使用静态访问器,没有它们你的XAML将无法编译) 相关的。net代码是静态函数DependencyPropertyDescriptor.FromProperty,在这里显示了我自己的注释(总结如下):
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类,静态或非静态,实际上甚至可以是一个结构体!