stringformat - WPF: ¿cómo usar 2 convertidores en 1 enlace?
ivalueconverter wpf (9)
Acabo de crear lo que he llamado ReversedBooleanToVisibilityConverter para básicamente hacer lo que esos 2 harían por usted pero en un solo paso.
Tengo un control que quiero mostrar / ocultar, dependiendo del valor de un booleano.
Tengo un NegatedBooleanConverter (cambia de verdadero a falso y viceversa) y necesito ejecutar este convertidor primero. Tengo BooleanToVisibilityConverter y necesito ejecutar este convertidor después de NegatedBoolConverter.
¿Como puedo solucionar este problema? Quiero hacer esto en XAML.
editar: esta es una posible solución.
Eso no parece funcionar. Primero convierte el valor con los convertidores separados y luego hace algo con los valores convertidos.
Lo que necesito es:
- Convierta el valor con el primer convertidor (esto da conversionValue).
- Convierta el valor convertido con el segundo convertidor y este es el resultado que necesito.
Ampliando la gran respuesta de Natrium ...
XAML
<conv:ConverterChain x:Key="convBoolToInverseToVisibility">
<conv:BoolToInverseConverter />
<BooleanToVisibilityConverter />
</conv:ConverterChain>
Clase
/// <summary>Represents a chain of <see cref="IValueConverter"/>s to be executed in succession.</summary>
[ContentProperty("Converters")]
[ContentWrapper(typeof(ValueConverterCollection))]
public class ConverterChain : IValueConverter
{
private readonly ValueConverterCollection _converters= new ValueConverterCollection();
/// <summary>Gets the converters to execute.</summary>
public ValueConverterCollection Converters
{
get { return _converters; }
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converters
.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converters
.Reverse()
.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
}
#endregion
}
/// <summary>Represents a collection of <see cref="IValueConverter"/>s.</summary>
public sealed class ValueConverterCollection : Collection<IValueConverter> { }
Creo que es posible que desee utilizar un Multiconverter aquí en lugar de dos convertidores por separado. Debería poder reutilizar la lógica de sus conversores existentes. Echa un vistazo a esta discusión para empezar.
En este caso, no necesita una cadena de conversión. Solo necesitas un convertidor configurable. Esto es similar a la respuesta anterior de Carlo, pero define explícitamente los valores verdadero y falso (lo que significa que puede usar los mismos convertidores para conversiones escondidas, visibles o contraídas).
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : IValueConverter
{
public Visibility TrueValue { get; set; }
public Visibility FalseValue { get; set; }
public BoolToVisibilityConverter()
{
// set defaults
FalseValue = Visibility.Hidden;
TrueValue = Visibility.Visible;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Luego en XAML:
<BoolToVisibilityConverter x:Key="BoolToVisibleConverter"
FalseValue="Hidden"
TrueValue="Visible" />
Esto es lo que hice:
public class CombiningConverter : IValueConverter
{
public IValueConverter Converter1 { get; set; }
public IValueConverter Converter2 { get; set; }
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
object convertedValue = Converter1.Convert(value, targetType, parameter, culture);
return Converter2.Convert(convertedValue, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
y lo llamo así:
<converters:CombiningConverter x:Key="negatedBoolToVisibilityConverter" Converter1="{StaticResource NegatedBooleanConverter}" Converter2="{StaticResource BoolToVisibilityConverter}" />
Un MultiValueConverter también podría ser posible, creo. Tal vez lo intente más tarde.
Lo que hacemos en nuestro proyecto es crear un BooleanToVisibilityConverter, dicho conversor toma un parámetro (cualquier cosa, una cadena, un int, bool, lo que sea). Si el parámetro está establecido, invierte el resultado; de lo contrario, arroja el resultado regular.
public class BooleanToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool? isVisible = value as bool?;
if (parameter != null && isVisible.HasValue)
isVisible = !isVisible;
if (isVisible.HasValue && isVisible.Value == true)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
Para abordar este problema específico, en lugar de utilizar dos conversores, podría escribir su propio BoolToVisibilityConverter que use el parámetro Converter (como bool) para determinar si se niega o no el booleano original.
Para responder a mi propia pregunta nuevamente: He estado utilizando esta solución desde hace años: http://www.codeproject.com/Articles/15061/Piping-Value-Converters-in-WPF
Hace un nuevo convertidor de 2 convertidores existentes, primero llamando al primero, luego al segundo, etc.
Estoy bastante satisfecho con esta solución.
Personalmente, solo crearía 1 convertidor único que realice la conversión completa. A menos que necesite desesperadamente los convertidores (como la negación) en otros lugares, será más fácil de mantener (imo) si la conversión se realiza una vez, en un solo lugar.