Как разрешить значение привязки данных внутри MarkupExtension? - PullRequest
5 голосов
/ 12 июня 2009

Я сделал расширение разметки для перевода строк на основе ключа. Пример

<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 имеет значение null. Я проверил свою привязку вот так

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

и работает нормально.

Есть идеи?

Ответы [ 2 ]

2 голосов
/ 17 июня 2009

не возможно получить значение привязки. Вы не должны даже пытаться сделать это. 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.

0 голосов
/ 04 января 2018

Ответ toxvaerd не универсален. Он ломается, если в исходной привязке уже был конвертер. Или при написании конвертера это невозможно.

Есть лучшее решение. Мы можем объявить два конструктора. Второй, принимающий BindingBase, будет вызван XAML при использовании привязки. Чтобы разрешить значение привязки, мы можем объявить частное вложенное свойство. Чтобы это работало, нам нужно знать целевой элемент расширения разметки.

Тут есть одна загвоздка: когда расширение разметки используется внутри шаблона, целевой элемент отсутствует (очевидно). В этом случае вы должны использовать return this в 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));
}
...