Cook Computing

A Generic Base Class For Implementing IValueConverter

January 23, 2010 Written by Charles Cook

I've written some Silverlight value converters recently and I thought it would be useful to implement a base class to make this task easier, also adding some type checking at runtime. This would enable a boolean to Visibility converter to be implemented like this:


public class BooleanToVisibilityConverter 
  : BaseValueConverter<bool, Visibility>
{
  protected override Visibility Convert(bool value, 
    CultureInfo culture)
  {
    return value ? Visibility.Visible : Visibility.Collapsed;
  }
}

The two generic parameters for BaseValueConverter are the type of the value to be converted and the target type. For case where a parameter is used a different base class is used, also called BaseValueConverter, which takes an extra generic parameter specifying the type of the parameter. For example, you may want the Visibility to be based on the inverse of the boolean value, and so the class can be implemented with an extra string generic parameter to specify that the inverse should be used:


public class BooleanToVisibilityConverter 
  : BaseValueConverter<bool, Visibility, String>
{
  protected override Visibility Convert(bool value, 
    string parameter, CultureInfo culture)
  {
    if (parameter != null && parameter == "!")
          value = !value;
    return value ? Visibility.Visible : Visibility.Collapsed;
  }
}

As usual, the CLR namespace needs to be defined, for example if the class is in a namespace called Converters:


  xmlns:c="clr-namespace:Converters" 

And the converter class specified as a resource:


  <UserControl.Resources>
    <c:BooleanToVisibilityConverter 
      x:Key="BooleanToVisibilityConverter"/>
  </UserControl.Resources>

Finally, the converter resource is used in a binding. In this example TextNotVisible is a boolean value in the control's DataContext and the parameter is used to indicate that whenTextNotVisible is true, the converter should return Visibility.Visible:


    <TextBlock Text="Hello World" Margin="20" Name="Txt"
      Visibility="{Binding TextNotVisible, 
        Converter={StaticResource BooleanToVisibilityConverter}, 
        ConverterParameter=!}"

This is the implementation of BaseValueConverter:


public abstract class BaseValueConverter<V, T, P> 
  : IValueConverter
{
  public object Convert(object value, Type targetType,
    object parameter, CultureInfo culture)
  {
    if (value.GetType() != typeof(V))
      throw new ArgumentException(GetType().Name
        + ".Convert: value type not " + typeof(V).Name);
    if (targetType != typeof(T))
      throw new ArgumentException(GetType().Name
        + ".Convert: target type not " + typeof(T).Name);
    return Convert((V)value, (P)parameter, culture);
  }
 
  public object ConvertBack(object value, Type targetType,
    object parameter, CultureInfo culture)
  {
    if (value.GetType() != typeof(T))
      throw new ArgumentException(GetType().Name
        + ".ConvertBack: value type not " + typeof(T).Name);
    if (targetType != typeof(V))
      throw new ArgumentException(GetType().Name
        + ".ConvertBack: target type not " + typeof(V).Name);
    return ConvertBack((T)value, (P)parameter, culture);
  }
 
  protected virtual T Convert(V value, P parameter,
    CultureInfo culture)
  {
    throw new NotImplementedException(
      GetType().Name + "Convert not implemented");
  }
 
  protected virtual V ConvertBack(T value,
    P parameter, CultureInfo culture)
  {
    throw new NotImplementedException(
      GetType().Name + "ConvertBack not implemented");
  }
}
 
public abstract class BaseValueConverter<V, T>
  : BaseValueConverter<V, T, object>
{
  protected virtual T Convert(V value, CultureInfo culture)
  {
    throw new NotImplementedException(
      GetType().Name + "Convert not implemented");
  }
 
  protected virtual V ConvertBack(T value, CultureInfo culture)
  {
    throw new NotImplementedException(
      GetType().Name + "ConvertBack not implemented");
  }
 
  protected sealed override T Convert(V value, 
    object parameter, CultureInfo culture)
  {
    if (parameter != null)
      throw new ArgumentException(GetType().Name
        + ".Convert: binding contains unexpected parameter");
    return Convert(value, culture);
  }
 
  protected sealed override V ConvertBack(T value, 
    object parameter, CultureInfo culture)
  {
    if (parameter != null)
      throw new ArgumentException(GetType().Name
        + ".ConvertBack: binding contains unexpected parameter");
    return ConvertBack(value, culture);
  }
}

UPDATE 27/07/2010: Fixed errors pointed out by Omar Alani.