Difference between Attached and non-Attached Dependency Properties in Silverlight
Asked Answered
C

5

13

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 find ANY difference between .Register and .RegisterAttached in Silverlight. Now, before you jump the gun and tell me that .RegisterAttached is used for attaching a DP to another class, try implementing an Attached Dependency Property using DependencyProperty.Register(). I have found not a single difference, and so I am at a loss as to what the difference is.
Furthermore, in my specific case, I'm attempting to extend the functionality of the Grid class, and want to give it some extra properties. As such, I've tried listing passing both typeof(Grid) and typeof(FluidLayoutManager) (the implementing class) as the ownerType parameter and it also seems to make very little difference... (I believe it does make a difference when I pass two custom classes from the same namespace. However when passing a Microsoft defined class vs. a custom class, I always have it showing up in the XAML as a DP of the custom class.)
Any clarification on this topic would be much appreciated, as I'm sitting here scratching my head, and wondering if there is any difference at all, or if Microsoft is just screwing with me once again.

Cotyledon answered 27/7, 2011 at 11:56 Comment(0)
S
10

Given the discussions flowing in comments I will try to do this one in plain English:

The main difference between Attached Dependency Properties and Dependency Properties (and therefore between .Register and .RegisterAttached) is that RegisterAttached allows the value to be assigned to any dependency object whereas Register only allows it to be attached to the class passed as the ownerType parameter.

As Haris Hasan mentions (deep in the comment thread), your example is using the only type allowed (i.e. CustomControl) and does not show you that the Attached version can be assigned to ANY dependency object.

e.g. you can do this with your Attached Dependency Property (but not a plain DP):

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

The best reference for ADPs I can find is this one: http://msdn.microsoft.com/en-us/library/ms749011.aspx

We used ADPs as the basis of a localisation system, so that translations could be parasited onto objects during load rather than using horrendously long bindings. Couldn't do that with DPs

Update:

I would also like to clarify that the parent limitation applies to XAML based use of the attribute. From code the parent limitation apparently does not apply.

Sinciput answered 27/7, 2011 at 14:47 Comment(4)
Further XAML info 1: XAML uses the SetDP/ADP() and GetDP/ADP() functions for both, however calling SetValue(DP/ADP, value) and GetValue(DP/ADP) do not. 2: In the XAML Designer, Intellisense will use the SetDP/ADP() and GetDP/ADP() method signatures to determine if the Dependency Property or Attached Dependency Property is shown. As such, it will show invalid Dependency Properties if the SetDP/ADP() parameters accept a DependencyObject instead of a more specific class.Cotyledon
+1 for that comment... now I just need to translate that into clear English as well and we're done :)Sinciput
So my next question is, are there any other differences between the two, and does the ownerType parameter passed in .RegisterAttached actually mean anything at all in Silverlight?Cotyledon
@Melodatron: Good question. I seem to remember something about attached properties being related to a parent of a particular type (and it is referred to as an Owner Type), but I don't see how that is actually used anywhere to restrict it. It actually looks like it is more for implying the relationship than enforcing it.Sinciput
E
8

It is wrong to believe that "RegisterAttached allows the value to be assigned to any dependency object whereas Register only allows it to be attached to the class passed as the ownerType parameter". Here is a perfectly working example of an attached property registered with 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 shows that the only difference between Register and RegisterAttached is that the Register throws out much of the supplied metadata and only preserves it for the instances of registering class (via OverrideMetadata). It means that attributes such as Inherits and various update notifications usually specified in the metadata do not work on properties registered with Register and attached to objects of other types (other than the registering type). So Register is actually a stripped down version of RegisterAttached. It was probably made this way for performance reasons.

In the example linked by Haris Hasan in the comments to his answer, if you change RegisterAttached to Register, the buttons stop moving (because the property no longer provides AffectsParentArrange metadata for Button type) but they are nevertheless redrawn in their new locations when you resize the window. But if you add the same metadata to the Button type after a call to InitializeComponent():

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

then everything works again as if RegisterAttached was called.

Edinburgh answered 18/9, 2011 at 19:13 Comment(1)
Just to clarify, the parent limitation I mentioned applies to use of the attributes in XAML, not in code.Sinciput
K
5

They might not be much different as far as implementation is concerned but they are difference in actions i.e. they are different in what they do and are used for.

Simple Register is used for simple dependency properties which you usually are used for bindings and validations so they are normal CLR properties with some additional magic which helps in WPF

RegisterAttached is normally used where you want to expose a property that can be accessed and set in the child class like DockPanel where children of control tells parent where they want to be placed using Dock.Left or Dock.Right. So they are kind of special dependency properties which can be accessed in the child controls (which is not the case with simple Register properties) and they(in case of DockPanel) helps parent control in displaying children

In short one cay say Register is used registering dependency properties which are used in same class while RegisterAttached is used for registering special dependency properties called attached properties and they are used and accessed by classes other than one which defined it

This is a good explanation of Attached Properties and what cannot be achieved through simple DP

Keepsake answered 27/7, 2011 at 12:5 Comment(8)
One additional note (that can come in handy): Attached property setters are called when Xaml containing the property is parsed. Dependency setters are only called by you (the rest of the time the properties are accessed solely by SetValue & GetValue.Sinciput
@Haris: Please consider the edit to the question I have made. I understand "Register/RegisterAttached is normally used for..." but that doesn't make a difference in terms of implementation or function, which is the question I want answered. Also, as you can see in the example, there is absolutely no problem with accessing a "Registered" DP from another class.Cotyledon
@Melodatron: problem is that you are trying to use the Attached Property in your example in a wrong context. You are using Attached Property where even simple dependency property will work. As I said Attached Property is a dependency property with additional features, that's why you are able to access it in same way. But in your example you are not using the actual power of Attached Properties. You are not accessing it from another class this is the main thing which differentiates it from simple dependency property.Keepsake
@Haris: I accept what you're saying, that ADPs are extended DPs, however what I'm saying, is that to me, at the moment, these "additional features" area complete mystery to me. If you could outline and/or make an example of something that ADPs can do that DPs can't, I would be very appreciative.Cotyledon
I don't have time to make an example at this time but internet is full of these. See blogs.msdn.com/b/bencon/archive/2006/07/24/677520.aspx for example. You cannot do it through normal DP and this is the place where ADP really comes handy and show their powerKeepsake
Another nice and simple example silverlightshow.net/items/…Keepsake
@Haris: While I found the first example interesting, it made me realise that I needed to clarify something that I had only mentioned in the tags, and not in the question description. This question is speifically directed at the Silverlight framework, and not the WPF framework. That aside, I changed the .RegisterAttached to .Register and it no longer functioned correctly. The major difference was that the positions of the buttons didn't update on being clicked, which I presume is to do with the this.InternalChildren and/or the FrameworkPropertyMetadata - neither of which exist in SL!Cotyledon
@Haris: Okay, so while I'm beginning to see a difference here and a difference there, and appreciate your help, I disagree that the silverlightshow example is a good example. All the information in it is lost amongst explanations. Like I said, I really want to know what the difference between the two are.Cotyledon
T
2

If you register with RegisterAttached, it becomes global as a property in the store of any DependencyObject, i.e. you could SetValue on any Dependency Object

If you use Register when Get/Setvalue are called there will be a check that the call is prom an object that is castable to the registering type.

An Example of a property that behaves like RegisterAttached is Grid.Row and Grid.Column.

Taillight answered 27/7, 2011 at 12:4 Comment(1)
Okay, so while I could scream for reading "An Example of a property that behaves like RegisterAttached is Grid.Row and Grid.Column." you actually have a point there. The only difference I have found so far is when using IncompatibleControl.SetValue() I am able to set and retrieve the Attached Dependency value in C#. The DP however, throws an error, and neither work in XAML.Cotyledon
C
1

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!

Conciliate answered 5/6, 2016 at 3:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.