Dominic Jonas
Dominic Jonas

Reputation: 5025

BindingExpressionBase is null in custom MarkupExtension

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.

C# Code

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;
    }
}

XAML Code

<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>

Upvotes: 2

Views: 870

Answers (1)

Evk
Evk

Reputation: 101613

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;

Upvotes: 3

Related Questions