Blend Behaviours - can you bind to their properties?
Asked Answered
L

2

7

I am currently migrating a number of attached behaviours I have created to Blend Behaviours so that they support drag and drop within Expression Blend. I have noticed that authors of Blend behaviours tend to define the behaviour properties as dependency properties.

I have created a behaviour, TiltBehaviour, which exposes a public dependency property, TiltFactor, of type double. Within Expression Blend I can set the value of this property, however, the option to add a "Data Binding ..." is grayed out:

cannot bind to behaviour property

I have also noticed that Behaviors extend DependencyObject, therefore they do not have a DataContext and therefore cannot inherit the DataContext of the element to which they are attached. This feels like a real weakness to me!

So, the bottom-line is, if I cannot set a binding to my behaviors dependency property in Blend, and it does not inherit a DataContext, why bother using dependency properties at all? I could just use CLR properties instead.

Lac answered 8/6, 2011 at 9:13 Comment(6)
That's very strange, what version of Blend it is? I have no problem binding either element properties or DataContext properties to dependency properties of Behaviors with Blend 4 + Silverlight 4...Microcircuit
Dain, I have tried binding manually within Visual Studio, e.g. TiltFactor="{Binding}" this results in AG_E_PARSER_BAD_PROPERTY_VALUE, which often indicates that you are binding to a non-dependency property. Can you point to an example on the web of binding a property of a behaviour?Lac
You have to bind to a concrete property of the right type: TiltFactor="{Binding TiltFactor}"Microcircuit
Dain ... it depends what your DataContext is, in my above example I was setting the DataContext of the element I was attaching to, to a double, e.g. Rectangle.DataContext = 10.0, therefore a binding of TiltFactor="{Binding}", without a property path is totally valid. Anyhow, I tried your suggestion TiltFactor="{Binding TiltFactor}", still no joy! .... perhaps it is a specific issue in VS / Silverlight for Windows Phone 7.Lac
Ah, sorry... Ran out of ideas, it works for me with SL4 even with implicitly inherited DataContext.Microcircuit
No problem - thanks for your help. I'll just have to give SL4 a try instead :-/ At least I know that it should work and that there is a perfectly good reason why these properties should be DPs.Lac
B
4

Edit: dain is correct you can still bind to the DataContext which is created artificially, how often have you seen people bind to a SolidColorBrush.Color? It also works even though SolidColorBrush inherits from DependencyObject and hence has no DataContext.

See this blog post on the inheritance context.

The thing is that since the behaviours are attached, they do not appear in the logical tree and hence would not inherit a DataContext anyway.

Boneblack answered 8/6, 2011 at 10:13 Comment(3)
+1 for the blog post on inheritance context. Why haven't I seen that before?! great stuff.Lac
However ... they could be made to inherit the DataContext, as per this blog post: scottlogic.co.uk/blog/colin/2010/05/…Lac
It's weird that this information is either not in the documentation or very hard to find.Boneblack
J
8

Blend behaviors would be almost useless unless they supported binding! I recreated your tilt behavior and it supports binding in Blend 4 with no problems so I don't know exactly where you went wrong. Perhaps you can reproduce my simple example and then infer what's wrong with your setup.

Here's the (non-functional) tilt behavior with dependency property:

public class TiltBehavior : Behavior<FrameworkElement>
{
    public double TiltFactor
    {
        get { return (double)GetValue(TiltFactorProperty); }
        set { SetValue(TiltFactorProperty, value); }
    }

    public static readonly DependencyProperty TiltFactorProperty =
        DependencyProperty.Register("TiltFactor", typeof(double), typeof(TiltBehavior), new UIPropertyMetadata(0.0));
}

Then just create a new window and drop the behavior onto the grid and Blend creates this:

<Grid>
    <i:Interaction.Behaviors>
        <local:TiltBehavior/>
    </i:Interaction.Behaviors>
</Grid>

and the Blend "Data Binding..." option is available in the properties tab.

I tested this with both WPF and Silverlight projects. The built-in behaviors, triggers and actions all support binding by virtue of using being dependency properties and all the Blend samples use binding heavily and so this has to work.

In fact you can just drop a built-in behavior like FluidMoveBehavior onto your grid and check that Duration, which is a dependency property, supports binding. If that doesn't work, I have no idea what's going on!


Let's consider then how binding works for these strange beasts called behaviors.

As WPF or Silverlight programmers we are very familiar with binding for things like FrameworkElement. It has a property called DataContext that we can manipulate to control the default binding source and that property is inherited by nested elements when we don't override it.

But behaviors (and triggers and actions) are not of type FrameworkElement. They are ultimately derived from DependencyObject, as we might expect. But while we can using binding on any class derived from DependencyObject, our familiar DataContext is missing at this low-level and so the binding has to supply the source. That's not very convenient.

So behaviors are derived (on WPF anyway) from Animatable and Animatable is derived from Freezable. The Freezable class is where the simplicity of dependency objects intersects with the complexity of framework elements. The Freezable class is also the base class for more familiar things like brushes and image sources. These classes don't need the full complexity of a framework element, but they want to participate, in a limited way with the elements that they are associated with.

Through a complicated magical process, Freezable instances acquire an inheritance context: the framework element they are most closely associated with, and when a default binding is used (one without a source), the Freezable uses the DataContext of it's associated element instead.

In fact as you learn about behaviors the AssociatedObject is a central concept; for a behavior it is the thing that the behavior is attached to. But the important point is that all Freezable objects can use the DataContext of their AssociatedObject by proxy.

All this magic is what Josh Smith calls the:

And so all this leads up to saying that due to the Hillberg Freezable Trick, Blend behaviors support binding using the data context of their associated element as the default source. As a result, bindings for behaviors seem to "just work" without any effort on our part. Behaviors are a thousand times more useful because of this.

Johore answered 14/6, 2011 at 5:54 Comment(0)
B
4

Edit: dain is correct you can still bind to the DataContext which is created artificially, how often have you seen people bind to a SolidColorBrush.Color? It also works even though SolidColorBrush inherits from DependencyObject and hence has no DataContext.

See this blog post on the inheritance context.

The thing is that since the behaviours are attached, they do not appear in the logical tree and hence would not inherit a DataContext anyway.

Boneblack answered 8/6, 2011 at 10:13 Comment(3)
+1 for the blog post on inheritance context. Why haven't I seen that before?! great stuff.Lac
However ... they could be made to inherit the DataContext, as per this blog post: scottlogic.co.uk/blog/colin/2010/05/…Lac
It's weird that this information is either not in the documentation or very hard to find.Boneblack

© 2022 - 2024 — McMap. All rights reserved.