9

How do you set a custom MarkupExtension from code?

You can easily set if from Xaml. The same goes for Binding and DynamicResource.

<TextBox FontSize="{Binding MyFontSize}"
         Style="{DynamicResource MyStyle}"
         Text="{markup:CustomMarkup}"/>

Setting the same values through code behind requires a little different approach

  1. Binding: Use textBox.SetBinding or BindingOperations.SetBinding

    Binding binding = new Binding("MyFontSize");
    BindingOperations.SetBinding(textBox, TextBox.FontSizeProperty, binding);
    
  2. DynamicResource: Use SetResourceReference

    textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle");
    
  3. CustomMarkup: How do I set a custom MarkupExtension from code? Should I call ProvideValue and it that case, how do I get a hold of a IServiceProvider?*

    CustomMarkupExtension customExtension = new CustomMarkupExtension();
    textBox.Text = customExtension.ProvideValue(??);
    

I found surprisingly little on the subject so, can it be done?


H.B. has answered the question. Just adding some details here to why I wanted to do this. I tried to create a workaround for the following problem.

The problem is that you can't derive from Binding and override ProvideValue since it is sealed. You'll have to do something like this instead: A base class for custom WPF binding markup extensions. But then the problem is that when you return a Binding to a Setter you get an exception, but outside of the Style it works fine.

I've read in several places that you should return the MarkupExtension itself if the TargetObject is a Setter to allow it to reeavaluate once it is being applied to an actual FrameworkElement and this makes sense.

However, that only works when the TargetProperty is of type object, otherwise the exception is back. If you look at the source code for BindingBase you can see that it does exactly this but it appears the framework has some secret ingredient that makes it work.

Community
  • 1
  • 1
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266

6 Answers6

6

I think there is no code-equivalent, the services are only available via XAML. From MSDN:

MarkupExtension has only one virtual method, ProvideValue. The input serviceProvider parameter is how the services are communicated to implementations when the markup extension is called by a XAML processor.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • 1
    Hey H.B. Yes, I read that too, however I was hoping that there would still be a way. This is very bad news and custom `MarkupExtensions` seems like a semi-working concept. They can't be used in a `Style` `Setter` if the TargetProperty isn't of type `object` so I was hoping to work around this issue by appling it myself in an attached behavior but there goes that plan. Anyway, thanks for your answer – Fredrik Hedblad Sep 20 '11 at 19:29
  • @Meleak: Well, as the name says MarkupExtensions extend markup, they are not really meant to be used in code. I cannot reproduce any problems with the use of MarkupExtensions in Setters by the way. – H.B. Sep 20 '11 at 21:22
  • 1
    I agree, this is more than implied by the name, just still hoped for a way :) I wasn't really clear in my comment, the problem is when you provide a `Binding` value to a `Setter`. You've answered the question and I accept your answer. Added some details to why I wanted to do this in my question – Fredrik Hedblad Sep 20 '11 at 22:14
6

What about this as an alternative, it is generated in code but not necessarily as elegant as XAML:

        var markup = new CustomMarkup();
        markup.ProvideValue(new Target(textBox, TextBox.TextProperty));

The implementation for Target is simply:

public struct Target : IServiceProvider, IProvideValueTarget
{
    private readonly DependencyObject _targetObject;
    private readonly DependencyProperty _targetProperty;

    public Target(DependencyObject targetObject, DependencyProperty targetProperty)
    {
        _targetObject = targetObject;
        _targetProperty = targetProperty;
    }

    public object GetService(Type serviceType)
    {
        if (serviceType == typeof(IProvideValueTarget))
            return this;
        return null;
    }

    object IProvideValueTarget.TargetObject { get { return _targetObject; } }
    object IProvideValueTarget.TargetProperty { get { return _targetProperty; } }
}

The only thing that remains is the ability to get a reference back to 'CustomMarkup' from the XAML object model. With the above you need to hang-on to a reference to it.

Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57
Fil
  • 1,766
  • 1
  • 15
  • 15
3

If your markup extension is fairly simple and creates a binding and returns the result from ProvideValue() then you can add a simple helper method:

public class CommandExtension : MarkupExtension
{
    public CommandExtension(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return GetBinding(this.Name).ProvideValue(serviceProvider);
    }

    static Binding GetBinding(string name)
    {
        return new Binding("Commands[" + name + "]") { Mode = BindingMode.OneWay };
    }

    public static void SetBinding(DependencyObject target, DependencyProperty dp, string commandName)
    {
        BindingOperations.SetBinding(target, dp, GetBinding(commandName));
    }
}

And then in code, you can just call CommandExtension.SetBinding() instead of BindingOperations.SetBinding().

Obviously, if you are doing anything more complex than this then this solution may not be appropriate.

Stephen Drew
  • 1,415
  • 19
  • 31
2

This Silverlight TV show might shed some light on this issue. I recall them showing some code samples that might be helpful.

Philipp Schmid
  • 5,778
  • 5
  • 44
  • 66
1

As H.B. pointed out, a MarkupExtension is only intended to be used within XAML.

What makes Binding unique is that it actually derives from MarkupExtension which is what makes it possible to use the extension syntax {Binding ...} or the full markup <Binding>...</Binding> and use it in code.

However, you can always try creating an intermediary object (something akin to BindingOperations) that knows how to use your custom markup extension and apply it to a target DependencyObject.

To do this, I believe you would need to make use of the XamlSetMarkupExtensionAttribute (for .NET 4) or the IReceiveMarkupExtension interface (for .NET 3.x). I am not entirely sure how to make use of the attribute and/or interface, but it might point you in the right direction.

sellmeadog
  • 7,437
  • 1
  • 31
  • 45
  • I'm not sure what you mean by "makes Binding unique", it derives from `MarkupExtension` and so does my custom `MarkupExtension`, I can't see the difference. For the second part of your answer you seem to be on the right track, that's good to know. +1 – Fredrik Hedblad Sep 20 '11 at 22:16
  • I was trying to convey is that `Binding` does not follow the common convention of creating `MyClass` accompanied by `MyClassExtension` which would typically provide a value of type `MyClass` in markup. – sellmeadog Sep 20 '11 at 23:20
  • Still not sure I follow but any `MarkupExtension` can be set like `{markup:Custom}` and ``. Anyway, it doesn't matter. I worked around my problem by using a `MultiBinding` in the end – Fredrik Hedblad Sep 20 '11 at 23:41
  • Yeah, I know many years past but... could you please share your workaround with multibinding? – Andrew Mikhailov Jun 19 '17 at 10:22
0

How do I set a custom MarkupExtension from code?

If you can modify it, then simply extract the logic into separate SomeMethod which can be called alone and/or from ProvideValue.

Then instead of

textBox.Text = customExtension.ProvideValue(??);

you just call it

customExtension.SomeMethod(textBox, TextBox.TextProperty);

Often we are creating custom property extensions (used like this in xaml):

<TextBox Text="{local:SomeExtension ...}" />

This can be written like this:

public class SomeExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var provider =     serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        var target = provider.TargetObject as DependencyObject;
        var property = provider.TargetProperty as DependencyProperty;
        // defer execution if target is data template
        if (target == null)
           return this;
        return SomeMethod(target, property);
    }

    public object SomeMethod(DependencyObject target, DependencyProperty property)
    {
        ... // do something
    }
}

Since I realized there is sometimes a necessity to use markup extensions from code I am always trying to write them in such a way.

Sinatr
  • 20,892
  • 15
  • 90
  • 319