Avoid exceptions from PropertyGrid

2019-08-14 12:12发布

问题:

In my sample I have used a PropertyGrid control and fetched my all properties and values.

When I change one property to an invalid value, like "123456789008765" for an integer field, it throws the following error:

I need to avoid this error, and if the given value is invalid I need to assign a default value (in this case 13, 13). How can I do this?

回答1:

Short Answer

The PropertyGrid uses the TypeConverter to convert a string to your property value and convert your property value to a string.

The error message is raised by the type converter of Size when converting the string to size value.

You need to change the behavior for your property when converting from string. You should create a custom type converter and register it for your property to handle the exception when converting from string to your property value.

Path to solution

Here is the main problems that you should solve:

  • Let the user type in the property grid.
  • Show the string representation of the property in property grid.
  • Let the user expand the property.
  • Instead of showing a message box, use the default value when the string can not be converted to your property value.

What you should know

The PropertyGrid uses the TypeConverter to convert a string to your property value and convert your property value to a string.

To customize the behavior of the property grid when converting a string to your property, you should Implement a Type Converter.

  • To convert a string to your property type, you should override CanConvertFrom and ConvertFrom. When you say the type can convert from string then you will allow the user to type in property grid.
  • To convert your property to string, you should override ConvertTo. This way you provide string representation of your property value.
  • To let the user to expand the property, you should derive your custom converter from ExpandableObjectConverter.
  • To register a converter for a property, you can use a TypeConverter attribute.
  • To get the a converter for a type, for example the default type converter for Size you can use TypeDescriptor.GetConverter(typeof(Size));

Solution

The error message is raise by the type converter of Size. So we need to change the behavior for your property when converting from string.

Based on above information we implement a custom type converter for your property. In the implementation we inherit from ExpandableObjectConverter, then in constructor we get the default converter for Size and use it when overriding the mentioned methods.

The main requirement is implemented in ConvertFrom and there we try to convert the string to Size and when an exception occurs instead we use the DefaultValue that you set using an attribute an if there is no default value, we use 0,0 as value.

Implementation

Create a converter:

public class CustomSizeConverter : ExpandableObjectConverter
{
    TypeConverter converter;
    public CustomSizeConverter()
    {
        converter = TypeDescriptor.GetConverter(typeof(Size));
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return converter.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        try
        {
            return converter.ConvertFrom(context, culture, value);
        }
        catch (Exception)
        {
            var d= context.PropertyDescriptor.Attributes.OfType<DefaultValueAttribute>().FirstOrDefault();
            if (d != null)
                return d.Value;
            else
                return new Size(0,0);
        }
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        return converter.ConvertTo(context, culture, value, destinationType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return converter.CanConvertTo(context, destinationType);
    }
}

And here is your class that contains a Size property with new converter:

public class MyObject
{
    public MyObject() { SomeSize = new Size(13, 13);  }
    public string SomeText { get; set; }

    [TypeConverter(typeof(CustomSizeConverter))]
    [DefaultValue(typeof(Size), "13,13")]
    public Size SomeSize { get; set; }
}

Don't forget to include required usings:

using System;
using System.Linq;
using System.Drawing;
using System.ComponentModel;