6

我制作了一个标记扩展,用于基于键翻译字符串。例子

<TextBlock Text="{Translate myKey}" />

现在我希望能够使用嵌套绑定来提供我的密钥。例子:

<TextBlock Text="{Translate {Binding KeyFromDataContext}}" />

当我这样做时,我得到一个 System.Windows.Data.Binding 对象。通过调用 ProvideValue 并传递 ServiceProvider,我可以获得一个 BindingExpression:

var binding = Key as Binding;
if (binding == null) {
    return null;
}
var bindingExpression = binding.ProvideValue(_serviceProvider) as BindingExpression;
if (bindingExpression == null) {
    return null;
}
var bindingKey = bindingExpression.DataItem;

我可以得到这个 bindingExpression,但 DataItem 属性为空。我已经像这样测试了我的绑定

<TextBlock Text="{Binding KeyFromDataContext}" />

它工作正常。

有任何想法吗?

4

2 回答 2

2

无法获取绑定的值。你甚至不应该尝试这样做。WPF 使用一些花哨的反射来解决绑定并相信我 - 你不想自己开始尝试。

无论如何,考虑到这一点,这就是我最终要做的,这实际上是一个很好的解决方案:

我做了一个TranslateConverter负责翻译的:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var key = value as string ?? parameter as string;

    if (key != null)
    {
    // Do translation based on the key

    }
    return null;
}

然后在我的TranslateExtension我只是这样做:

var binding = Key as Binding ?? new Binding{Mode = BindingMode.OneWay};
binding.Converter = new TranslateConverter(_targetObject, _targetProperty, Dictionary, Converter);
binding.ConverterParameter = Key is Binding ? null : Key as string;

return binding.ProvideValue(serviceProvider);

这样,一个绑定由 WPF 解析并作为值传递给转换器,而简单的文本键作为参数传递给转换器。

_targetObject_targetProperty从 ServiceProvider 获得。

于 2009-06-17T11:24:20.910 回答
1

toxvaerd的答案并不普遍。如果原始绑定已经有转换器,它会中断。或者当无法编写转换器时。

有一个更好的解决方案。我们可以声明两个构造函数。当使用绑定时,第二个接受BindingBase将由 XAML 调用。要解析绑定的值,我们可以声明一个私有附加属性。为此,我们需要知道标记扩展的目标元素。

有一个问题:当在模板中使用标记扩展时,没有目标元素(显然)。在这种情况下,您应该使用return thisin ProvideValue()- 这样在应用模板时将再次调用扩展。

public class TranslateExtension : MarkupExtension
{
    private readonly BindingBase _binding;

    public TranslateExtension(BindingBase binding)
    {
        _binding = binding;
    }

    public TranslateExtension(string key)
    {
        Key = key;
    }

    [ConstructorArgument("key")]
    public string Key { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (_binding != null)
        {
            var pvt = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
            var target = pvt.TargetObject as DependencyObject;

            // if we are inside a template, WPF will call us again when it is applied
            if (target == null)
                return this; 

            BindingOperations.SetBinding(target, ValueProperty, _binding);
            Key = (string)target.GetValue(ValueProperty);
            BindingOperations.ClearBinding(target, ValueProperty);
        }

        // now do the translation using Key
        return ...;
    }

    private static readonly DependencyProperty ValueProperty = 
        DependencyProperty.RegisterAttached("Value", typeof(string), typeof(TranslateExtension));
}
于 2018-01-04T06:45:01.267 回答