Difference between Attached and non-Attached Dependency Properties in Silverlight

后端 未结 5 1314
心在旅途
心在旅途 2020-12-17 17:09

Okay Stackers, I\'ve spent a good couple of hours on this question, and I want to know if anybody has a definitive answer.
For all the research I\'ve done, I can\'t fin

5条回答
  •  庸人自扰
    2020-12-17 17:35

    So exactly what is 'ownerType' used for in RegisterAttached? This issue has been nagging at me for a few years, so I finally took a closer look at 4.6.1 code in WindowsBase.

    For any DependencyProperty, attached or otherwise, what it eventually comes down to is what type of PropertyDescriptor WPF obtains for late-bound XAML access, and this isn't determined until the first time (on a per type/property pairing basis) such access is attempted. This deferral is necessary because PropertyDescriptor encapsulates a property bound to a specific type, whereas the point of attached properties is to avoid exactly this.

    By the time XAML access occurs, the Register(...) versus RegisterAttached(...) distinction has been lost to the mists of (run)time. As others have noted on this page, 'DependencyProperty' itself doesn't encode a distinction between an attached vs. non- variety. Every DP is assumed to be eligible for either usage, subject only to what can be figured out at runtime.

    For example, the .NET code below seems to rely on not finding a matching instance property on the 'tOwner' type as the first requirement for allowing attached access. To confirm this diagnosis, it then checks whether 'tOwner' exposes one of the static access methods. This is a vague check insofar as it doesn't verify the method signatures. Those signatures only matter for XAML access; all actual runtime targets for the attached property must be DependencyObjects, which WPF accesses through DependencyObject.GetValue/SetValue whenever possible. (The VS Designer reportedly does use the static accessors, and your XAML won't compile without them)

    The relevant .NET code is the static function DependencyPropertyDescriptor.FromProperty, shown here with my own comments (summary below):

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

    Summary: When calling RegisterAttached to create an attached DependencyProperty, the only thing 'ownerType' is used for is to identify a type which defines appropriate static Get/Set accessors. At least one of those accessors must exist on 'ownerType' or XAML attached access will not compile or silently fail. Although RegisterAttached does not fail in this case, and instead successfully returns a defunct DP. For DPs intended for attached use only, 'tOwner' doesn't have to derive from DependencyObject. You can use any regular .NET class, static or non-static, and in fact can even be a struct!

提交回复
热议问题