Sunday, September 2, 2012

Common Converters in WPF/Silverlight

In WPF/Silverlight many times we have to provides a way to apply custom logic to binding. In this situation Converters are very handy to use. For converters we have to create a class which implements IValueConverter class. Below are few common Converters used in WPF/Silverlight.

Byte Array to Image Converter

    public class ByteToImageConverter : IValueConverter
    {
        public BitmapImage ConvertByteArrayToBitMapImage(byte[] imageByteArray)
        {
            BitmapImage img = new BitmapImage();
            using (MemoryStream memStream = new MemoryStream(imageByteArray))
            {
                img.SetSource(memStream);
            }
            return img;
        }


        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            BitmapImage img = new BitmapImage();
            if (value != null)
            {
                img = this.ConvertByteArrayToBitMapImage(value as byte[]);
            }
            else
            {
                //img = new BitmapImage(new Uri("/AssemblyName;component/Images/defaultImage.jpg", UriKind.Relative));
                 img = null;
            }
            return img;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

Example
        <Image Margin="3" Source="{Binding Path=ByteArray, Converter={StaticResource byteToImageConverter}}"/>
ByteToImageConverter will convert byte array of image to a BitmapImage which can be used in Source property of an image. This can be used when we have an image saved in binary form in database and we want to bind that and show in image control. We can show a default image if byte array is null by uncommenting the code in “else” part of BitmapToImageConverter class.

Null or Empty Visibility Converter

    public class NullEmptyVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {    
                if (value == null)
                {
                    return Visibility.Collapsed;
                }
                else if (value.GetType() == typeof(string) && string.IsNullOrWhiteSpace(value.ToString()) == true)
                {
                    return Visibility.Collapsed;
                }
                else
                {
                    return Visibility.Visible;
                }    
        }

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

Example
        <TextBlock Margin="3" Text="{Binding Path=Data, Converter={StaticResource nullVisibilityConverter}}"/>

NullEmptyVisibilityConverter can be used if we don’t want to show the control if value in binding is null. In above class, we are setting Visibility property as Collapsed if value is null or if string type value is null or empty.

Negative Converter

Public Class NegativeConverter
    Implements IValueConverter
    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        If value.[GetType]() Is GetType(Boolean) Then
            Dim result As Boolean = CBool(value)
            Return Not result
        Else
            Return value
        End If
    End Function


    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New Exception("Not implemented")
    End Function
End Class

Example
        <StackPanel Orientation="Vertical">
            <CheckBox  HorizontalAlignment="Left" Margin="3" Width="100" Height="25" Name="chkFirst"/>
            <CheckBox Name="chkSecond" HorizontalAlignment="Left" Margin="3" Height="25" IsChecked="{Binding Path=IsChecked, ElementName=chkFirst, Converter={StaticResource negativeConverter}}"/>
        </StackPanel>
Sometime we want to display reverse result of the binded value. For example, we want to disable the control if value is true. Now the disable control we have to set IsEnabled = false and we have value of true to disable. So in this case we can use above NegativeConverter.

In above example code, we are unchecking the chkSecond checkbox if chkFirst checkbox is checked and vice versa. So for this we are setting staticResource of NegativeConverter in binding converter property.

Multiplication Converter

Public Class MultiplyConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        If parameter IsNot Nothing Then
            Dim result As Double = Double.Parse(parameter.ToString())
            Return CDbl(value) * result
        Else
            Return CDbl(value)
        End If
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New Exception("Not implemented")
    End Function
End Class

Example
 <StackPanel Orientation="Vertical">
            <TextBox HorizontalAlignment="Left" Margin="3" Width="100" Height="25" Name="txtFirst"/>
            <TextBox Name="txtSecond" HorizontalAlignment="Left" Margin="3" Height="25" Width="{Binding Path=ActualWidth, ElementName=txtFirst, Converter={StaticResource multiplyConverter}, ConverterParameter=2.0}"/>
 </StackPanel>

In the above code in txtSecond textbox we are binding  it’s width property to txtFirst textbox width property. So we have set ElementName as txtFirst and Path as ActualWidth. And we want to have txtSecond width double of txtFirst. So we would be setting staticresource of MultiplyConverter in converter property and “2.0” as ConverterParameter property.

Now in MultiplyConverter class we would have ActualWidth of txtFirst in value parameter and “2.0” in “parameter” parameter. So we will multiply the two value and return the result.

Divide Converter

Public Class DivideConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        If parameter IsNot Nothing Then
            Dim result As Double = Double.Parse(parameter.ToString())

            If result > 0 Then
                Return CDbl(value) / result
            Else
                Return CDbl(value)
            End If

        Else
            Return CDbl(value)
        End If
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New Exception("Not implemented")
    End Function
End Class

Example
 <StackPanel Orientation="Vertical">
            <TextBox HorizontalAlignment="Left" Margin="3" Width="100" Height="25" Name="txtFirst"/>
            <TextBox Name="txtSecond" HorizontalAlignment="Left" Margin="3" Height="25" Width="{Binding Path=ActualWidth, ElementName=txtFirst, Converter={StaticResource divideConverter}, ConverterParameter=2.0}"/>
 </StackPanel>

Similar to Multiplication Converter,  in the above code in txtSecond textbox we are binding  it’s width property to txtFirst textbox width property. So we have set ElementName as txtFirst and Path as ActualWidth. And we want to have txtSecond width half of txtFirst. So we would be setting staticresource of DivideConverter in converter property and “2.0” as ConverterParameter property.

Now in DivideConverter class we would have ActualWidth of txtFirst in value parameter and “2.0” in “parameter” parameter. So we will divide the ActualWidth by “2.0” and return the result.

Subtract Converter

Public Class SubtractConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        If parameter IsNot Nothing Then
            Dim result As Double = Double.Parse(parameter.ToString())
            Return CDbl(value) - result
        Else
            Return CDbl(value)
        End If
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New Exception("Not implemented")
    End Function
End Class

Example
<StackPanel Orientation="Vertical">
            <TextBox HorizontalAlignment="Left" Margin="3" Width="100" Height="25" Name="txtFirst"/>
            <TextBox Name="txtSecond" HorizontalAlignment="Left" Margin="3" Height="25" Width="{Binding Path=ActualWidth, ElementName=txtFirst, Converter={StaticResource subtractConverter}, ConverterParameter=15.0}"/>
</StackPanel>
Here we want txtSecond textbox, 15 pixels less than txtFirst.  In above code in txtSecond textbox we are binding  it’s width property to txtFirst textbox width property. So we have set ElementName as txtFirst and Path as ActualWidth. And as we want to have txtSecond 15 pixels less than txtFirst, we would be setting staticresource of SubtractConveter in converter property and “15.0” as ConverterParameter property.

Now in SubtractConverter class we would have ActualWidth of txtFirst in value parameter and “15.0” in “parameter” parameter. So we will subtract 15 from  ActualWidth and return the result.

Note:

In all the above converters we have to create it’s instance in resource and reference it using their key. For example you can write following code to create instance of SubtractConverter.
<Window.Resources>
          <local:SubtractConverter x:Key="subtractConverter" />
</Window.Resources>