We use own and third party cookies to improve our services and show related advertising to your preferences by analyzing your browsing habits. If you continue browsing, we consider that you accept their use. Can change the settings or get more information here

Ver sitio en español Go to homepage Contact me
viernes, 10 de junio de 2016

The PropertyGrid control, type converters

To conclude this series on the PropertyGrid control I will show you how to use different type converters derived from the TypeConverter class to customize how they are displayed or edited the properties. The .NET framework provides converters for many types of properties, but you can also easily implement your own converters by deriving a class from TypeConverter or a specialized converter and overriding a few functions.

If you prefer to start this series from the beginning, in this link you have the article on the basics of the PropertyGrid control.

In this link you can download the source code of the final version of the PropertyGrid demo project that I will use in this article, written in CSharp with Visual Studio 2013.

Standard converters

To begin, let's apply one of the standard .NET Framework converters that will allow us to expand the members of a property of a complex type to edit them one by one separately.

The class used is ExpandableTypeConverter, and, as always, we will use an attribute to decorate the property we want to expand, in this case of the Shape class. The TypeConverter attribute:

[CategoryGlobal("CAT_CUSTOMEDITORS")]
[DisplayNameGlobal("NAME_SHAPEPROPERTY")]
[DescriptionGlobal("DESC_SHAPEPROPERTY")]
[TypeConverter(typeof(ExpandableObjectConverter))]
[Editor(typeof(ShapeEditor), typeof(UITypeEditor))]
public Shape ShapeProperty { get; set; }

And this is the aspect in the PropertyGrid control:

Property members expanded
Property members expanded

You can see that the properties of the Shape class are also translated, as they also have globalized attributes.

Custom type converters

The PropertyGrid control allows you to edit the object properties using simple text editors. This means that you should implement a type converter that transforms a string to the type of the property, to process and accept user input, and the property type to a string in order to display the value in the control.

The procedure is simple. First, we derive a class from TypeConverter, or any of its derived classes, and then we override the necessary methods to implement the functionality we want. Let's see an example of converter that will allow us to show the user a collection property with the IEnumerable interface as a list with the three initial values comma separated, the EnumerableTypeConverter class.

Override the CanConvertFrom method to indicate that the type converter can convert from a string object:

public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}

Simply return true when the type indicated in sourceType be one of those accepted by your converter.

Another method you have to override is ConvertFrom. This method is used when you have to convert the user input to the corresponding property type:

public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value.GetType() == typeof(string))
{
return context.PropertyDescriptor.GetValue(context.Instance);
}
return base.ConvertFrom(context, culture, value);
}

In this case, we do not want to allow the user to change the value of the property by directly editing it, but simply to show the list in a custom format, so we return the current value of the property, obtained from the PropertyDescriptor of the context parameter. The Instance property of the context contains the object that is currently editing in the control.

The other method you must override is ConvertTo, which provides a custom string constructed from the current value of the property:

public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value, Type destinationType)
{
if (destinationType == typeof(string))
{
IEnumerable ienum = value as IEnumerable;
if (ienum != null)
{
int cnt = 0;
string result = "";
string c = "";
foreach (object obj in ienum)
{
if (cnt < 3)
{
result += c + obj.ToString();
c = ", ";
}
cnt++;
if (cnt > 3)
{
result += "...";
break;
}
}
return result;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}

In this case, we test that the object passed in value implements the IEnumerable interface and then we create a text string with the first three items in the collection separated by commas. If the collection contains more than three elements, we add three points at the end of the string to indicate this.

As usual, we decorate the property with the TypeConverter attribute:

[CategoryGlobal("CAT_COMPOSITEPROPERTIES")]
[DisplayNameGlobal("NAME_COLLECTIONPROPERTY")]
[DescriptionGlobal("DESC_COLLECTIONPROPERTY")]
[TypeConverter(typeof(EnumerableTypeConverter))]
public List<string> CollectionProperty { get; set; }

And this is the result obtained in the PropertyGrid:

Customized collection property
Customized Collection Property

Display a list of allowed values to the user

Another utility that can be obtained from the type converters is to show the user a drop-down list of allowed values for the property. In this example we will edit a property of type int from a list of values, using the IntRangeTypeConverter class.

The first method to override is GetStandardValuesSupported, you simply must return true:

public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}

If you also want, as in this case, that the user can only select a number from the list, you have to override GetStandardValuesExclusive and return true:

public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}

The list of values is provided by the overridden GetStandardValues method, which returns an object of type StandardValuesCollection constructed from a collection that should be of the same type as the type of property edited, in this case, int:

public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(new int[6] { 0, 10, 20, 30, 40, 50 });
}

As in the previous case, we must also override the CanConvertFrom, ConvertFrom and ConvertTo methods.

We decorate the property in which we want to apply the converter:

[CategoryGlobal("CAT_SIMPLEPROPERTIES")]
[DisplayNameGlobal("NAME_INTPROPERTY")]
[DescriptionGlobal("DESC_INTPROPERTY")]
[DefaultValue(0)]
[TypeConverter(typeof(IntRangeTypeConverter))]
public int IntProperty { get; set; }

And this is the result in the PropertyGrid control:

Int property with a limited set of values
Int Property with a limited set of values

Translating enum values

The last converter that we will implement allows us to show the user the property values of type enum translated to the current language. In this case we will derive from the EnumConverter class, in order to take advantage of the base functionality of this class specialized in enumerations. We use the resource file to provide the translated text strings for each member of the enumeration.

Although we can implement a generic converter, in this case we will implement a specialized one in ValueEnum type enumerations, indicating that in the class constructor:

public class ValueEnumTranslateConverter : EnumConverter
{
public ValueEnumTranslateConverter()
: base(typeof(ValueEnum))
{
}

As in the previous examples, the CanConvertFrom, ConvertFrom and ConvertTo methods must be overridden. In ConvertFrom, we compare user input with the enumeration member translations to obtain the corresponding enumeration value:

public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
string v = value as string;
if (v != null)
{
if (string.Compare(v, Resources.VE_ONE, true) == 0)
{
return ValueEnum.One;
}
else if (string.Compare(v, Resources.VE_TWO, true) == 0)
{
return ValueEnum.Two;
}
else if (string.Compare(v, Resources.VE_THREE, true) == 0)
{
return ValueEnum.Three;
}
else
{
return context.PropertyDescriptor.GetValue(context.Instance);
}
}
return base.ConvertFrom(context, culture, value);
}

And in ConvertTo, convert the property value in the corresponding string to the current language:

public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
switch ((ValueEnum)value)
{
case ValueEnum.One:
return Resources.VE_ONE;
case ValueEnum.Two:
return Resources.VE_TWO;
default:
return Resources.VE_THREE;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}

We decorate the corresponding property with the TypeConverter attribute:

[CategoryGlobal("CAT_COMPOSITEPROPERTIES")]
[DisplayNameGlobal("NAME_ENUMPROPERTY")]
[DescriptionGlobal("DESC_ENUMPROPERTY")]
[DefaultValue(ValueEnum.One)]
[TypeConverter(typeof(ValueEnumTranslateConverter))]
public ValueEnum EnumProperty { get; set; }

And this is the result in the PropertyGrid control:

Translated enum property
Translated enum Property
Share this article: Share in Twitter Share in Facebook Share in Google Plus Share in LinkedIn
Comments (0):
* (Your comment will be published after revision)

E-Mail


Name


Web


Message


CAPTCHA
Change the CAPTCHA codeSpeak the CAPTCHA code