2009-06-12 14 views
5

He creado una extensión de marcado para traducir cadenas basadas en una clave. Ejemplo¿Cómo puedo resolver el valor de un enlace de datos dentro de una extensión de marca?

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

Ahora quiero poder utilizar enlaces anidados para proporcionar mis claves. Ejemplo:

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

Cuando hago esto, obtengo un objeto System.Windows.Data.Binding. Llamando ProvideValue y que pasa por el Proveedor del Servicio puedo conseguir un 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; 

puedo conseguir este BindingExpression, pero la propiedad DataItem es nulo. He probado mi enlace como este

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

y funciona bien.

¿Alguna idea?

Respuesta

0

El toxvaerd's answer no es universal. Se rompe si el enlace original ya tenía un convertidor. O cuando escribir un convertidor no es posible.

Hay una solución mejor. Podemos declarar dos constructores. El segundo que acepte BindingBase será llamado por XAML cuando se use un enlace. Para resolver el valor del enlace, podemos declarar una propiedad privada adjunta. Para que esto funcione, necesitamos conocer el elemento de destino de la extensión de marcado.

Hay una trampa: cuando la extensión de marcado se utiliza dentro de una plantilla, no hay un elemento de destino (obviamente). En este caso, usted es supposed para usar return this en ProvideValue() - de esta manera se llamará nuevamente a la extensión cuando se aplique la plantilla.

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)); 
} 
+0

¿Qué pasa si el valor de enlace cambia, ProvideValue no se vuelve a llamar, correcto? ¿Hay alguna solución para esto? – Haytam

+0

@Haitam, la pregunta era sobre la resolución del valor del enlace. Si sospecha que podría cambiar, puede devolver otro enlace que se una a 'Valor' y use un convertidor. – torvin

+0

¿Puede explicarnos cómo funciona la propiedad adjunta? Siempre obtengo 'null' de' target.GetValue() '... – spacer

2

Es no posible obtener el valor de un enlace. Se supone que ni siquiera debes tratar de hacer esto. WPF usa un reflejo sofisticado para resolver los enlaces y confiar en mí, no querrás empezar a probarlo tú mismo.

De todos modos, con esto en mente, esto es lo que terminé haciendo, que en realidad es una buena solución:

hice un TranslateConverter que se encargó de la traducción:

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

Luego, en mi TranslateExtension simplemente hacer esto:

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

esta manera una unión se resuelve con WPF y se pasa al convertidor como valor, mientras que un simple la clave de texto se pasa al convertidor como un parámetro.

_targetObject y _targetProperty se obtienen de ServiceProvider.

+4

¿Qué significa eso? Un enlace de datos es uno y solo lo usa para transportar un valor y mantenerlo actualizado. ¿Cómo puede un enlace no tener un valor entonces? Estoy buscando la misma solución pero no respondiste tu pregunta, respondiste otra cosa. Un convertidor no es una opción para mí. – ygoe

+0

@ygoe revisa mi [respuesta] (https: // stackoverflow.com/a/48090008/332528) – torvin

Cuestiones relacionadas