XAML Converter awesomeness

By | July 11, 2012

Ok so here’s the thing. I’m not going to be putting “Visibility” properties in the Models of my MVVM architecture in my apps.

I would, however, expose some Boolean value on the ViewModel that might say “HasChildren”, etc.
But the problem is GETTING those things to actually show or hide elements on the screen.
Enter XAML Converters.
By putting the following in your View:

	<phone:PhoneApplicationPage.Resources>
		<Converters:BoolToVisibleConverter x:Key="trueToVisible" />
	</phone:PhoneApplicationPage.Resources>
...
	<Grid Visibility="{Binding HasChildren, Converter={StaticResource trueToVisible}}">
...
	</Grid>

You end up quickly with a Grid that shows/hides itself based off a Boolean property value. Sweet.
Here’s what the codebehind of a converter looks like:

    public class BoolToVisibleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            bool visibility = System.Convert.ToBoolean(value);
            return visibility ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Visibility visibility = (Visibility)value;
            return (visibility == Visibility.Visible);
        }
    }

That’s a pretty practical usage, actually. What about something a bit more customized? I had the need in one of my apps for taking the size of a file and converting it to a format that humans understand. E.g.: 19546721 -> 19.5MB.
How would you do this? Sure you could make your VM do some sweet code, but then you’re stuck doing that for your next project. And the next, and the next. How about letting the View take care of it?

	public enum ByteUnit
	{
		B = 0,
		KB = 1024,
		MB = 1048576,
		GB = 1073741824
	}

	public class ByteStringConverter : IValueConverter
	{
		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			var numOfBytes = System.Convert.ToInt64(value);
			var precision = parameter != null ? System.Convert.ToInt16(parameter) : 2;
			if (numOfBytes <= 0 && precision < 0)
			{
				return string.Empty;
			}

			precision = Math.Max(0, precision);
			if (numOfBytes >= (int)ByteUnit.GB)
			{
				return string.Concat(Math.Round(numOfBytes / (double)ByteUnit.GB, precision), "GB");
			}
			else if (numOfBytes >= (int)ByteUnit.MB)
			{
				return string.Concat(Math.Round(numOfBytes / (double)ByteUnit.MB, precision), "MB");
			}
			else if (numOfBytes >= (int)ByteUnit.KB)
			{
				return string.Concat(Math.Round(numOfBytes / (double)ByteUnit.KB, precision), "KB");
			}

			return string.Concat(numOfBytes, "B");
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			var stringVal = (string)value;
			var pos = stringVal.IndexOf("GB", StringComparison.OrdinalIgnoreCase);
			if (pos != -1)
			{
				return long.Parse(stringVal.Substring(0, pos)) * (int)ByteUnit.GB;
			}
			else
			{
				pos = stringVal.IndexOf("MB", StringComparison.OrdinalIgnoreCase);
				if (pos != -1)
				{
					return long.Parse(stringVal.Substring(0, pos)) * (int)ByteUnit.MB;
				}
				else
				{
					pos = stringVal.IndexOf("KB", StringComparison.OrdinalIgnoreCase);
					if (pos != -1)
					{
						return long.Parse(stringVal.Substring(0, pos)) * (int)ByteUnit.KB;
					}

					return long.Parse(stringVal.Substring(0, stringVal.Length - 1));
				}
			}
		}
	}

Before you go and criticize the sizes of the enum values, stop.
Now that I’ve got this in place, in any View I want this to happen now, I just slap in this beauty:

	<phone:PhoneApplicationPage.Resources>
		<Converters:ByteStringConverter x:Key="toByteString" />
	</phone:PhoneApplicationPage.Resources>
...
	<TextBlock Text="{Binding Size, Converter={StaticResource toByteString}, ConverterParameter=-1}" />

Which turns the size in to a pretty string of text.

The source code for a collection I’ve built in my Windows Phone development thus far is attached. By using what you’ve seen here you should be able to figure out how to use the other ones, and write your own.

Converters.zip