2010-04-09 6 views
105

Tengo una situación en la que necesito mostrar un valor entero, con destino a una propiedad en mi contexto de datos, después de ponerlo a través de dos conversiones separadas:¿Hay alguna forma de encadenar convertidores de valores múltiples en XAML?

  1. inversa del valor dentro de un rango (por ejemplo, rango es de 1 a 100; valor en DataContext es 90, el usuario ve el valor de 10)
  2. convertir el número en una cadena

me di cuenta que podía hacer las dos etapas mediante la creación de mi propio convertidor (que implementa IValueConverter). Sin embargo, ya tengo un convertidor de valor por separado que hace solo el primer paso, y el segundo paso está cubierto por Int32Converter.

¿Hay alguna manera de encadenar estas dos clases existentes en XAML sin tener que crear una clase adicional que las agregue?

Si necesito aclarar algo de esto, háganmelo saber. :)

Gracias.

Respuesta

50

encontrado exactamente lo que estaba buscando, por cortesía de Josh Smith: Piping Value Converters(archive.org link).

Define una clase ValueConverterGroup, cuyo uso en XAML es exactamente lo que estaba esperando. He aquí un ejemplo:

<!-- Converts the Status attribute text to a SolidColorBrush used to draw 
    the output of statusDisplayNameGroup. --> 
<local:ValueConverterGroup x:Key="statusForegroundGroup"> 
    <local:IntegerStringToProcessingStateConverter /> 
    <local:ProcessingStateToColorConverter /> 
    <local:ColorToSolidColorBrushConverter /> 
</local:ValueConverterGroup> 

Gran materia. Gracias, Josh. :)

+1

En esta solución, cada convertidor debe tratar únicamente con un tipo (debe declararse en el atributo único ValueConversion). La solución @Town también puede hacer frente a los multiconversores. –

+5

publique la implementación; de lo contrario, linkrot –

4

Sí, hay formas de encadenar convertidores, pero no se ve bonito y no lo necesita aquí. Si alguna vez viene a necesitar esto, pregúntese ¿realmente es el camino a seguir? Simple siempre funciona mejor incluso si tiene que escribir su propio convertidor.

En su caso particular, todo lo que necesita hacer es formatear un valor convertido a una cadena. StringFormat propiedad en un Binding es tu amigo aquí.

<TextBlock Text="{Binding Value,Converter={StaticResource myConverter},StringFormat=D}" /> 
+0

se StringFormat trabajo en TwoWay? – diimdeep

+2

Si usa enlaces fuertemente, escribir un convertidor personalizado para convertidores de cadena termina con toneladas de convertidores tontos para todo tipo de configuraciones. En ese caso, la respuesta aceptada es una solución maravillosa. –

165

Utilicé this method por Gareth Evans en mi proyecto de Silverlight.

Aquí es mi implementación de la misma:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter 
{ 
    #region IValueConverter Members 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture)); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    #endregion 
} 

que luego puede ser utilizado en XAML como esto:

<c:ValueConverterGroup x:Key="InvertAndVisibilitate"> 
    <c:BooleanInverterConverter/> 
    <c:BooleanToVisibilityConverter/> 
</c:ValueConverterGroup> 
+3

¿Es lo mejor, para una implementación de ConvertBack hacer una copia de la colección y revertirla, y luego agregarla sobre eso? Entonces ConvertBack sería 'return this.Reverse (). Aggregate (value, (current, converter) => converter.ConvertBack (current, targetType, parameter, culture));' –

+5

@DLeh Esto no es realmente elegante como no funciona Proporciona a todos los conversores el tipo de objetivo final en lugar del tipo de objetivo correcto ... –

+0

Esto también funciona en UWP, ¡yay! –

0

Town's implementation del Gareth Evans's Silverlight project es grande, sin embargo, no es compatible con diferentes parámetros del convertidor.

Lo modifiqué para que pueda proporcionar parámetros, delimitados por comas (a menos que los escape, por supuesto).

Convertidor:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter 
{ 
    private string[] _parameters; 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if(parameter != null) 
      _parameters = Regex.Split(parameter.ToString(), @"(?<!\\),"); 

     return (this).Aggregate(value, (current, converter) => converter.Convert(current, targetType, GetParameter(converter), culture)); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    private string GetParameter(IValueConverter converter) 
    { 
     if (_parameters == null) 
      return null; 

     var index = IndexOf(converter as IValueConverter); 
     string parameter; 

     try 
     { 
      parameter = _parameters[index]; 
     } 

     catch (IndexOutOfRangeException ex) 
     { 
      parameter = null; 
     } 

     if (parameter != null) 
      parameter = Regex.Unescape(parameter); 

     return parameter; 
    } 
} 

Nota: ConvertBack no está implementado aquí, vea mi Gist para una versión actualizada.

Implementación:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:converters="clr-namespace:ATXF.Converters;assembly=ATXF" x:Class="ATXF.TestPage"> 
    <ResourceDictionary> 
    <converters:ValueConverterGroup x:Key="converters"> 
     <converters:ConverterOne /> 
     <converters:ConverterTwo /> 
    </converters:ValueConverterGroup> 
    </ResourceDictionary> 

    <Label Text="{Binding InitialValue, Converter={StaticResource converters}, ConverterParameter='Parameter1,Parameter2'}" /> 
</ContentPage> 
Cuestiones relacionadas