Custom Controls Design Time Support Part 6: Custom TypeConverter 2

The last post in the series I have showed you how to implement a custom TypeConverter and override some of the virtual methods from the base class.

In this post I'll show how to some more advanced examples on how to get the most of TypeConverters. First look at the strucure of the example.

[more] 

[code:c#]

public enum Processor
{
  TwoPointFour,
  TwoPointEight,
  ThreePointTwo,
  ThreePointSix
}

public class Computer
{
  private Processor _processorSpeed;
  public Processor ProcessorSpeed
  {
    get { return _processorSpeed; }
    set { _processorSpeed = value; }
  }

  private ComputerType _type;
  public ComputerType Type
  {
    get { return _type; }
    set { _type = value; }
  }
}

public abstract class ComputerType
{
  private int _hardDiskSize;
  public int HardDiskSize
  {
    get { return _hardDiskSize; }
    set { _hardDiskSize = value; }
  }
}

public class DesktopPC : ComputerType
{
  private bool _hasInternalUPS;
  public bool HasInternalUPS
  {
    get { return _hasInternalUPS; }
    set { _hasInternalUPS = value; }
  }
}

public class Laptop : ComputerType
{
  private bool _hasBluetooth;
  public bool HasBluetooth
  {
    get { return _hasBluetooth; }
    set { _hasBluetooth = value; }
  }
}

public class PocketPC : ComputerType
{
  private bool _canMakeCalls;
  public bool CanMakeCalls
  {
    get { return _canMakeCalls; }
    set { _canMakeCalls = value; }
  }
}

[/code]

Now to setup a test enivronment I made a new windows forms application added a Button and a PropertyGrid control, on button click.

 

[code:c#]

Computer toshiba = new Computer();
toshiba.ProcessorSpeed = Processor.TwoPointEight;
toshiba.Type = new Laptop();

propertyGrid1.SelectedObject = toshiba;

[/code]

And this is how it looks.

Now we need to enhance how that looks, first lets change how the Processor enum appears. The enum appears like that as the default behaviour of the EnumConverter which shows the enum values as is, to change that behaviour lets start by deriving from the EnumConverter and changing how it converts to and from a string.

[code:c#]

public class ProcessorConverter : EnumConverter
{
  private Type _enumType;
  private Dictionary _enumValues;

  public ProcessorConverter(Type enumType) : base(enumType)
  {
    _enumType = enumType;
    if (_enumValues == null)
    {
      _enumValues = new Dictionary();
      _enumValues.Add(Processor.TwoPointFour, "2.4 Ghz");
      _enumValues.Add(Processor.TwoPointEight, "2.8 Ghz");
      _enumValues.Add(Processor.ThreePointTwo, "3.2 Ghz");
      _enumValues.Add(Processor.ThreePointSix, "3.6 Ghz");
    }
  }

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object src)
  {
    if (src is string)
    {
      if (_enumValues.ContainsValue((string)src))
      {
        foreach (KeyValuePair item in _enumValues)
        {
          if(item.Value == (string)src)
            return item.Key;
        }
      }
      return null;
    }
    return base.ConvertFrom(context, culture, src);
  }
  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object src, Type destinationType)
  {
    if (destinationType == typeof(string) && src is Processor)
    {
      return _enumValues[(Processor)src];
    }
    return base.ConvertTo(context, culture, src, destinationType);
  }
}

[/code]

As you can notice when deriving from the EnumConverter you have to implement the constructor having enumtype parameter. Now don't forget to associate the ProcessorConverter with the Processor enum.

[code:c#]

[TypeConverter(typeof(ProcessorConverter))]
public enum Processor

[/code]

And here is the result.

Now lets implement a ComputerTypeConverter, I will derive from the ExpandableObjectConverter which is already useful for listing the inner properties.

[code:c#]

public class ComputerTypeConverter : ExpandableObjectConverter

[/code]

By applying this converter as it is without implementing any custom code gives the following result. Now lets first override the CanConvert methods to allow our Converter to work with strings.

[code:c#]

public class ComputerTypeConverter : ExpandableObjectConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    if (sourceType == typeof(string))
    {
      return true;
    }
    return base.CanConvertFrom(context, sourceType);
  }
  public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
  {
    if (destinationType == typeof(string))
    {
      return true;
    }
    return base.CanConvertTo(context, destinationType);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object src)
  {
    if (src is string)
    {
      switch ((string)src)
      {
        case "Desktop PC": return new DesktopPC();
        case "Laptop": return new Laptop();
        case "Pocket PC": return new PocketPC();
        case "(None)":
        default:
          return null;
      }
    }
    return base.ConvertFrom(context, culture, src);
  }
  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object src, Type destinationType)
  {
    if (destinationType == typeof(string))
    {
      if(src == null) return "(None)";
      if(src is DesktopPC) return "Desktop PC";
      if(src is Laptop) return "Laptop";
      if(src is PocketPC) return "Pocket PC";
    }
    return base.ConvertTo(context, culture, src, destinationType);
  }
}

[/code]

What we have done here is kind of nice, now we can write the string representing the computer type in the property grid and the TypeConverter automatically converts it for us. But the only problem here is that if the user/developer might not memorize all computer types and with the exact letter case, so we need a way to make it easy on them. Yes, its time to override the GetStandardValues method.

[code:c#]

private ComputerType[] _standardValues = new ComputerType[] { null, new DesktopPC(), new Laptop(), new PocketPC() };
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
  return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
  return new StandardValuesCollection(_standardValues);
}

[/code]

The final looks like this.

 

So, this is the end of TypeConverters I hope it become useful to someone someday. Next parts I will talk about UITypeEditors.

Computer.cs (5.67 kb)        

3 thoughts on “Custom Controls Design Time Support Part 6: Custom TypeConverter 2”

  1. Hi Amr,

    Your articles really saved me alot of research. Quick question on this part. How do you persist the changes to the dynamic properties (ie. DesktopPC, Laptop and PocketPC)? I added PersistenceMode(PersistenceMode.InnerProperty and DesignerSerializationVisiblity(…Content) to the Type property in the Computer class but I am getting an error when I return to the page in Designer mode or in the Source. It seems the subclass specific properties (ie. for Laptop class: HasBlueTooth) is not valid for class ComputerType in the Type property of the Computer class.

    Thanks,

    Geoffrey

  2. Hello Geoffrey,
    When I developed this part it was on a windows forms control,not for web I didnt try it on web controls yet,I might soon but I hope I can find some time to manage this.

    Regards.

Leave a Reply

Your email address will not be published. Required fields are marked *