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.
Leave a Reply