Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set custom MarkupExtension from code

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.

  • Markup Extension in Data Trigger
  • Huge limitation of a MarkupExtension
  • A base class for custom WPF binding markup extensions (in the comments)

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.

like image 830
Fredrik Hedblad Avatar asked Sep 20 '11 18:09

Fredrik Hedblad


2 Answers

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.

like image 76
H.B. Avatar answered Oct 15 '22 21:10

H.B.


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.

like image 25
Fil Avatar answered Oct 15 '22 19:10

Fil