Question

We have two externally-defined, logically exclusive (business-wise) attached properties, both of which are inheritable. Depending on which was set closest to the DependencyObject we're reading them on, that determines which we use in our logic as seen below.

Note: I don't want to know that it is inherited which you can do via DependencyPropertyHelper.GetValueSource, I want to know from where, so I can determine precedence.

Consider the following visual tree hierarchy and the attached properties:

Root // <-- AProp1 first set here
    Child
        GrandChild // <-- AProp2 first set here
            GreatGrandChild // <-- AProp1 re-applied here
                GreatGreatGrandChild
  • In the case of Root, AProp1 is set alone therefore we use AProp1.

  • In the case of Child, AProp1 is inherited therefore we still use AProp1.

  • In the case of GrandChild, AProp2 is set, but AProp1 also has a value thanks to inheritance. However, since AProp2 is set on this object directly (i.e. distance of '0' levels away) vs AProp1 (distance of '2' away), AProp2 takes precedence to our business logic.

  • In the case of GreatGrandChild, again since AProp1 is set (Distance of '0' away), that takes precedence over AProp2 (distance of '1' away)

  • Finally, in the case of GreatGreatGrandChild, again, both AProp1 and AProp2 are picked up via inheritance, however since AProp1 was set closer in the hierarchy (distance of '1' away) vs AProp2 (distance of '2' away), AProp1 is the one we want to use.

If I can find the source, I know I can simply walk the chain and count the distance away. I just need the source.

Note:

For the record, yes, they can both be set at the same level, in which case AProp2 would take precedence, but that's irrelevant to this question.

A property can also be re-applied (see GreatGrandChild) even using the same value as a way to bump its priority. That's why I need to know where the source is/how far it is from the element I'm checking.

Was it helpful?

Solution 2

I think I've found it. One of those 'Right in front of you all along' things. It revolves around using DependencyPropertyHelper.GetValueSource to determine if a property value for an object is inherited or not.

That by itself of course doesn't tell you enough, but it's easy to go from there.

With that knowledge, you simply write a method which takes in the object in question and checks for the existence of both properties (remember, they may be 'set' because of a default value, style, template or whatever) and proceed accordingly:

  • If only one of the two is set, that's the one you use and you're done. Process as needed.

  • If they are both set, use the same precedence that a single property uses. (i.e. Local beats Inherit, Inherit beats Default, etc.) You're just comparing the values of two properties sources against each other at the same level.

  • If their source is the same and it is not Inherit, you use the one that takes precedence in your business logic (AProp2 in our case.)

  • If they are the same and it is Inherit, you walk up the logical tree (or visual depending on your need) and recursively call the same function passing in the parent.

Of course if this property is subject to animations and other such things, then the logic becomes a little more complex, but in our case, it's either going to be set locally or inherited. Not really much else.

Pretty simple now that I'm looking at it. Hope this helps others!

OTHER TIPS

A very interesting problem! You could add an attached property to DependencyObject that stores a type (you could store object reference if type is no good to you), we'll use it to record the type of object that the property was set on.

 public class DependencyPropertyExtension
    {
        public static Type GetAprop1Owner(DependencyObject obj)
        {
            return (Type)obj.GetValue(Aprop1OwnerProperty);
        }

        public static void SetAprop1Owner(DependencyObject obj, Type value)
        {
            obj.SetValue(Aprop1OwnerProperty, value);
        }

        public static readonly DependencyProperty Aprop1OwnerProperty = DependencyProperty.RegisterAttached("Aprop1Owner", typeof(Type), typeof(DependencyProperty), new UIPropertyMetadata(null));
    }

then in the depenency property Aprop1 you add some UIPropertyMetaData so we can respond to updates.

public object Aprop1
{
    get { return (object)GetValue(Aprop1Property); }
    set { SetValue(Aprop1Property, value); }
}
public static readonly DependencyProperty Aprop1Property =
    DependencyProperty.Register("Aprop1", typeof(object), typeof(Level1), new UIPropertyMetadata(null, new PropertyChangedCallback( (s, e) =>
        {
            DependencyPropertyExtension.SetAprop1Owner(s, s.GetType());
        })));

All I'm doing here is updating the attached property to the type of the object that the property is being set on.

you can read this information back out of any object with

DependencyPropertyExtension.GetAprop1Owner(testObject).FullName

Use a Tuple to contain both values. The closest inherited property will contain one value or the other, and it will take precedence.

You eliminate the need to reflect over the whole visual tree, and rely on the built in mechanics of dependency properties.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top