So as other have said, please first consider using a ValueConverter. This is the proper approach for manipulating bindings.
If however, you still want to use a MarkupExtension with bindings to the view-model or data context then you can create the binding manually in the markup extension class. This is similar to the approach taken by @nicolay.anykienko but we don't need to create an attached property.
As an example, I have created a currency symbol markup extension. The default behaviour is to use the CultureInfo.CurrentCulture
but a few view-models have their own CultureInfo property that are different from the current culture. So for these view-models the XAML needs to bind to this property. Note that this could easily be done with a Converter instead, but for the sake of an example here is the markup extension:
public class CurrencySymbolExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
var targetProvider = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetElement = targetProvider.TargetObject as FrameworkElement;
var targetProperty = targetProvider.TargetProperty as DependencyProperty;
if (!String.IsNullOrEmpty(CultureBindingPath) &&
targetElement != null &&
targetProperty != null)
{
// make sure that if the binding context changes then the binding gets updated.
targetElement.DataContextChanged +=
(sender, args) => ApplyBinding(targetElement, targetProperty, args.NewValue);
// apply a binding to the target
var binding = ApplyBinding(targetElement, targetProperty, targetElement.DataContext);
// return the initial value of the property
return binding.ProvideValue(serviceProvider);
}
else
{
// if no culture binding is provided then use the current culture
return CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol;
}
}
private Binding ApplyBinding(DependencyObject target, DependencyProperty property, object source)
{
BindingOperations.ClearBinding(target, property);
var binding = new Binding(CultureBindingPath + ".NumberFormat.CurrencySymbol")
{
Mode = BindingMode.OneWay,
Source = source,
FallbackValue = CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol,
};
BindingOperations.SetBinding(target, property, binding);
return binding;
}
public string CultureBindingPath { get; set; }
}
This then gets used as follows:
<!-- Standard Usage -->
<TextBlock Text="{local:CurrencySymbol}"/>
<!-- With DataContext Binding -->
<TextBlock Text="{local:CurrencySymbol CultureBindingPath=ViewModelCulture}"/>
Where ViewModelCulture
is the property on the view-model being used as the source of the binding.