views:

106

answers:

2

I'm trying to write a custom MarkupExtension that allows me to use my own mechanisms for defining a binding, however when I attempt to return a MultiBinding from my MarkupExtension I get the above exception.

I have:

<TextBlock Text="{my:CustomMarkup ...}" />

CustomMarkup returns a MultiBinding, but apparently Text doesn't like being set to a MultiBinding. How come it works when I say:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding ... />
    </TextBlock.Text>
</TextBlock>

But it doesn't work the way I'm doing it?

A: 

Did some investigating, and it turns out I'm supposed to actually set the binding in the MarkupExtension's ProvideValue method and then return the current value of the binding. A little counter-intuitive but so far seems to be working!

Here's roughly what I ended up with:

public override object ProvideValue( IServiceProvider serviceProvider ) {
    IProvideValueTarget valueProvider = (IProvideValueTarget)serviceProvider.GetService( typeof( IProvideValueTarget ) );
    // only need to do this if they're needed in your logic:
    object @target = valueProvider.TargetObject;
    object @property = valueProvider.TargetProperty;

    MultiBinding result = new MultiBinding();

    // set up binding as per custom logic...

    return result.ProvideValue( serviceProvider );
}

Add a little logic, dust lightly with error handling and serve.

Update: Turns out MultiBinding.ProvideValue() hooks up the binding itself, based on the target and property information in serviceProvider. That's much cleaner.

chaiguy
I think MultiBinding.ProvideValue already performs the BindingOperations.SetBinding... so you're doing it twice
Thomas Levesque
P.S. I just use the '@' symbols to make the variables look special... serves no functional purpose.
chaiguy
Wait, how could it? It doesn't know what the binding is until I set it.
chaiguy
Oh I see, it gets them from serviceProvider... interesting.
chaiguy
You're doing too much. To get the binding set, you only need to return a BindingExpressionBase (NOT a BindingBase) from your ProvideValue.
Ray Burns
You're right, it works, cool. That's a bit cleaner at least. I will update the answer.
chaiguy
+1  A: 

Don't return the MultiBinding itself. Instead, return the result of MultiBinding.ProvideValue.

BTW, what exactly are you doing in your markup extension ? Perhaps you could just inherit from MultiBinding, if you don't need to override ProvideValue (which is sealed). You can achieve almost anything by just setting the appropriate Converter and other properties

Thomas Levesque
Just what I was going to write, but you beat me to it.
Ray Burns
Interesting. My custom extension essentially gives me a way to define bindings as results of a method in my code-behind, and gets the dependencies for the binding from custom attributes I attach to the method. It's pretty cool if I don't say so myself. :)I'm thinking of publishing it if it ends up working out well.
chaiguy
I published my custom extension under the MIT license: http://hip.codeplex.com/
chaiguy