I have a CustomMarkupExtension
class. The Binding is working (!= null), but the BindingExpressionBase
is always null
.
Can someone explain me why? I need to get the BindingExpressionBase
to call the UpdateTarget()
method.
public class CustomMarkupExtension : MarkupExtension
{
public BindingBase Binding { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (Binding == null) return null;
BindingExpressionBase expression = Binding.ProvideValue(serviceProvider) as BindingExpressionBase;
if (expression != null)
{
expression.UpdateTarget();
}
return expression;
}
}
<DataGrid ItemsSource="{Binding Path=Alarms, RelativeSource={RelativeSource TemplatedParent}}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Extensions:CustomMarkupExtension Binding={Binding Number}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
The reason for that is you are using markup extension inside a template (DataTemplate
in this case). When your markup extension is parsed by xaml parser - control is not yet created and there is no target for a binding. In such case you should return your markup extension itself (return this
). If you do it - it will be applied again each time control is created using that template.
Built-in Binding
is of course follows this pattern too, that is why it returns itself (Binding
) and not BindingExpression
when target object is not yet available. So to fix, you have to do this:
public class CustomMarkupExtension : MarkupExtension
{
public BindingBase Binding { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider) {
if (Binding == null) return null;
var result = Binding.ProvideValue(serviceProvider);
var expression = result as BindingExpressionBase;
if (expression != null) {
expression.UpdateTarget();
return expression;
}
// no expression - return self to apply it again later
return this;
}
}
If you will create custom markup extension which does not rely on other extension (like Binding
in this case), you can check if you have target by doing this:
var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
bool hasTarget = target.TargetObject is DependencyObject;