Monday, July 07, 2008

Work process: How to use and build type converters

Today, I want to start new tag - “Work process”. Here I’m going to publish partial classes, fixes, small utilities, I’m building for myself or clients to help in work process. I’ll give an example: currently, I’m working on BiDi support for Silverlight 2.0 (beta 2 to RTM). During the work process, I need to write different classes, such as converters, string utilities, exception helpers etc.

ZK4Y7556 
© Imaginary copyright by  Noel Hendrickson

So, today I’ll public general type converter for generic classes.

What is type converter?

TypeConverter is a service attribute, used to help rendering engine to convert XAML string (usually it strings) to the type, you require in your class implementation. Here an example:

In your custom class you have dependency property of type TextAlignment

public TextAlignment TextAlignment
       {
           get { return (TextAlignment)GetValue(TextAlignmentProperty); }
           set { SetValue(TextAlignmentProperty, value); }
       }

In XAML code it will be used as:

<MyControl TextAlignment=”Left”/>

But, what you’re actually transfer to your class is string “Left”, when you need enum TextAlignment.Left, how to convert it? This for we’re using type converters

Attribute usage

In order to “tell” framework to use your type converter, you should mark target property with special attribute TypeConverterAttribute. Also, you can provide default value to your property by using another attribute DefaultValueAttribute. This will looks as following:

[TypeConverter(typeof(MyTextAlignmentTypeConverter))]
[DefaultValue(TextAlignment.Left)]
public TextAlignment TextAlignment
{
  get { return (TextAlignment)GetValue(TextAlignmentProperty); }
  set { SetValue(TextAlignmentProperty, value); }
}

How to build type converter

In order to build type converter, all you have to do is to build your own class, derived from TypeConverter. It also can be generic class. Then, implement necessary methods, that incorporate your business logic. Like this one, converter any enum value back and forward for TypeConverterAttribute

public class EnumValueConverter<T> : TypeConverter where T:struct
    {
        static EnumValueConverter()
        {
            if (!typeof(T).IsEnum) { Exceptions.ThrowInvalidOperationException("Cannot use this type for conversion"); }
        }

        public override bool CanConvertFrom(Type sourceType)
        {
            return Type.GetTypeCode(sourceType) == TypeCode.String;
        }

        public override object ConvertFrom(object value)
        {
            if (value == null)
            {
                typeof(T).ThrowConvertFromException(value);
            }
            if (value is string)
            {
                return ConvertFromString(value as string);
            }
            return (T)value;
        }

        public override object ConvertFromString(string text)
        {
            return (T)Enum.Parse(typeof(T), text, true);
        }

        public override string ConvertToString(object value)
        {
            return Enum.GetName(typeof(T), value);
        }
    }

Final result

Now, when we built converter and know how to use it, we can easily set it to any DependencyProperty requires conversion from / to enum

[TypeConverter(typeof(EnumValueConverter<LineStackingStrategy>))]
public LineStackingStrategy LineStackingStrategy
{
  get { return (LineStackingStrategy)GetValue(LineStackingStrategyProperty); }
  set { SetValue(LineStackingStrategyProperty, value); }
}

[TypeConverter(typeof(EnumValueConverter<TextAlignment>))]
[DefaultValue(TextAlignment.Right)]
public TextAlignment TextAlignment
  {
   get { return (TextAlignment)GetValue(TextAlignmentProperty); }
   set { SetValue(TextAlignmentProperty, value); }
  }

[TypeConverter(typeof(EnumValueConverter<TextWrapping>))]
public TextWrapping TextWrapping
{
  get { return (TextWrapping)GetValue(TextWrappingProperty); }
  set { SetValue(TextWrappingProperty, value); }
}

We done, have a nice day and be good people.

Stay tuned for release of BiDi support for Silverlight 2.0, sponsored by Development Platform Evangelism unit of Microsoft Israel

No comments: