BindingExpressionBase 在自定义 MarkupExtension 中为 null

BindingExpressionBase is null in custom MarkupExtension

我有一个CustomMarkupExtensionclass。绑定正在工作(!= null),但 BindingExpressionBase 始终是 null.

谁能解释一下为什么?我需要获取 BindingExpressionBase 来调用 UpdateTarget() 方法。

C# 代码

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

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

原因是您在模板中使用标记扩展(在本例中为 DataTemplate)。当您的标记扩展被 xaml 解析器解析时 - 尚未创建控件并且没有绑定目标。在这种情况下,您应该 return 您的标记扩展本身 (return this)。如果您这样做 - 每次使用该模板创建控件时都会再次应用它。

内置 Binding 当然也遵循这种模式,这就是为什么当目标对象是 return 本身 (Binding) 而不是 BindingExpression尚不可用。所以要修复,你必须这样做:

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

如果您要创建不依赖于其他扩展的自定义标记扩展(如本例中的 Binding),您可以通过执行以下操作检查您是否有目标:

var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
bool hasTarget = target.TargetObject is DependencyObject;