Define custom MarkupExtension from code

How do you install custom MarkupExtension from code?

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

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

Setting the same values ​​with the code behind requires a slightly different approach

  • Binding: Use textBox.SetBinding or BindingOperations.SetBinding

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

     textBox.SetResourceReference(TextBox.StyleProperty, "MyStyle"); 
  • CustomMarkup: How to install custom MarkupExtension from code? Should I call ProvideValue , in which case, how can I get IServiceProvider ? *

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

I found surprisingly little on this, can this be done?


HB answered the question. Just add some details here, why I wanted to do this. I tried to create a workaround for the following problem.

The problem is that you cannot get Binding and override ProvideValue , as it is sealed. You will need to do something like this: Base class for custom WPF markup extensions . But then the problem is that when you return Binding to Setter , you get an exception, but outside Style it works fine.

I read in several places that you should return MarkupExtension itself if TargetObject is a Setter so that it can reeavaluate as soon as it applies to the actual FrameworkElement , and that makes sense.

However, this only works when TargetProperty is of type object , otherwise an exception will be thrown. If you look at the source code for the BindingBase , you will see that it does just that, but it seems that there is some secret ingredient in the framework that makes it work.

+7
source share
6 answers

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

MarkupExtension has only one virtual ProvideValue method. The serviceProvider input parameter parameter is how services are passed in the implementation when the markup extension is invoked by the XAML processor.

+6
source

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

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

Realization of the whole goal:

 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 return the link to "CustomMarkup" from the XAML object model. With the above, you need to hang a link to it.

+4
source

If the markup extension is quite simple and creates a binding and returns the result from ProvideValue (), 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 something more complex, then this solution may not be acceptable.

+3
source

This Silverlight TV show may shed light on this issue. I remember how they showed some code examples that might be useful.

+2
source

Like HB that MarkupExtension is for use in XAML only.

What makes Binding unique is that it actually comes from MarkupExtension , which allows you to use the extension syntax {Binding ...} or the full markup <Binding>...</Binding> and use it in your code.

However, you can always try to create an intermediate object (something like BindingOperations ) that knows how to use its own markup extension and apply it to the target DependencyObject .

To do this, I believe that you will need to use XamlSetMarkupExtensionAttribute (for .NET 4) or IReceiveMarkupExtension (for .NET 3.x). I'm not quite sure how to use the attribute and / or interface, but it might point you in the right direction.

+1
source

How to install custom MarkupExtension from code?

If you can change it, just extract the logic into a separate SomeMethod , which can be called independently and / or from ProvideValue .

Then instead

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

you just call it

 customExtension.SomeMethod(textBox, TextBox.TextProperty); 

Often we create custom property extensions (used 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 that sometimes it becomes necessary to use markup extensions from code, I always try to write them this way.

0
source

All Articles