How can I use TypeConverters with a ConfigurationSection?
Asked Answered
S

4

5

So I've got a ConfigurationSection/ConfigurationElementCollection that has a configuration like this:

<mimeFormats>
    <add mimeFormat="text/html" />
</mimeFormats>

And here is how I handle the mimeFormats:

 public class MimeFormatElement: ConfigurationElement
{
    #region Constructors
    /// <summary>
    /// Predefines the valid properties and prepares
    /// the property collection.
    /// </summary>
    static MimeFormatElement()
    {
        // Predefine properties here
        _mimeFormat = new ConfigurationProperty(
            "mimeFormat",
            typeof(MimeFormat),
            "*/*",
            ConfigurationPropertyOptions.IsRequired
        );
    }
    private static ConfigurationProperty _mimeFormat;
    private static ConfigurationPropertyCollection _properties;

    [ConfigurationProperty("mimeFormat", IsRequired = true)]
    public MimeFormat MimeFormat
    {
        get { return (MimeFormat)base[_mimeFormat]; }
    }
}

public class MimeFormat
{
    public string Format
    {
        get
        {
            return Type + "/" + SubType;
        }
    }
    public string Type;
    public string SubType;

    public MimeFormat(string mimeFormatStr)
    {
        var parts = mimeFormatStr.Split('/');
        if (parts.Length != 2)
        {
            throw new Exception("Invalid MimeFormat");
        }

        Type = parts[0];
        SubType = parts[1];
    }
}

And obviously I need a TypeConverter that actually does something (instead of this empty shell):

public class MimeFormatConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        throw new NotImplementedException();
    }
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        throw new NotImplementedException();
    }
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        throw new NotImplementedException();
    }
}

How do I set up a TypeConverter that will allow type conversion from/to string? I've tried using the MSDN examples but I keep getting error message:

TypeConverter cannot convert from System.String.

Essentially, how can it be set up so that it will just work with whatever ConfigurationSection is trying to do?

Spina answered 9/5, 2011 at 16:37 Comment(0)
S
1

I figured it out. Here is the solution:

public class MimeFormatElement: ConfigurationElement
{
    #region Constructors
    /// <summary>
    /// Predefines the valid properties and prepares
    /// the property collection.
    /// </summary>
    static MimeFormatElement()
    {
        // Predefine properties here
        _mimeFormat = new ConfigurationProperty(
            "mimeFormat",
            typeof(MimeFormat),
            "*/*",
            ConfigurationPropertyOptions.IsRequired
        );

        _properties = new ConfigurationPropertyCollection {
            _mimeFormat, _enabled
        };
    }
    private static ConfigurationProperty _mimeFormat;
    private static ConfigurationPropertyCollection _properties;

    [ConfigurationProperty("mimeFormat", IsRequired = true)]
    public MimeFormat MimeFormat
    {
        get { return (MimeFormat)base[_mimeFormat]; }
    }
}

/*******************************************/
[TypeConverter(typeof(MimeFormatConverter))]
/*******************************************/
public class MimeFormat
{
    public string Format
    {
        get
        {
            return Type + "/" + SubType;
        }
    }
    public string Type;
    public string SubType;

    public MimeFormat(string mimeFormatStr)
    {
        var parts = mimeFormatStr.Split('/');
        if (parts.Length != 2)
        {
            throw new Exception("Invalid MimeFormat");
        }

        Type = parts[0];
        SubType = parts[1];
    }
}

public class MimeFormatConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return new MimeFormat((string)value);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string);
    }
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        var val = (MimeFormat)value;
        return val.Type + "/" + val.SubType;
    }
}
Spina answered 9/5, 2011 at 18:14 Comment(1)
You don't need to get the TypeConverter manually. Delete TypeDescriptor.GetConverter.... As you did, assiging the TypeConverter to the property or type itself is enough.Litigate
C
4

You can put TypeConverterAttribute on the property to tell the serializer how to handle it.

[TypeConverter(typeof(MimeFormatConverter))]
[ConfigurationProperty("mimeFormat", IsRequired = true)]
public MimeFormat MimeFormat
{
    get { return (MimeFormat)base[_mimeFormat]; }
}
Crowe answered 15/10, 2014 at 16:49 Comment(0)
L
3

Try this:

TestSection.cs

public class TestSection : ConfigurationSection
{

    private static readonly ConfigurationProperty sFooProperty = new ConfigurationProperty("Foo",
                                                                                          typeof(Foo),
                                                                                          null,
                                                                                          new FooTypeConverter(),
                                                                                          null,
                                                                                          ConfigurationPropertyOptions.None);

    public static readonly ConfigurationPropertyCollection sProperties = new ConfigurationPropertyCollection();

    static TestSection()
    {
        sProperties.Add(sFooProperty);
    }

    public Foo Foo
    {
        get { return (Foo)this[sFooProperty]; }
        set { this[sFooProperty] = value; }
    }

    protected override ConfigurationPropertyCollection Properties
    {
        get { return sProperties; }
    }

}

Foo.cs

public class Foo
{

    public string First { get; set; }
    public string Second { get; set; }

    public override string ToString()
    {
        return First + ',' + Second;
    }

}

FooTypeConverter.cs

public class FooTypeConverter : TypeConverter
{

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

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        string val = value as string;

        if (val != null)
        {
            string[] parts = val.Split(',');

            if (parts.Length != 2)
            {
                // Throw an exception
            }

            return new Foo { First = parts[0], Second = parts[1] };
        }

        return null;
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType == typeof(string));
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        Foo val = value as Foo;

        if (val != null)
            return val.ToString();

        return null;
    }

}
Litigate answered 9/5, 2011 at 17:4 Comment(6)
doesn't work. I still get the error message: The value of the property 'mimeFormat' cannot be parsed. The error is: TypeConverter cannot convert from System.String.Spina
@David Murdoch, I have updated. The above code worked for me.Litigate
I realized I left out an important piece of information; I'm actually setting the ConfigurationProperties in the ConfigurationElement's constructor. I'm editing my question to reflect this.Spina
I got it to work. There were a couple things additional things I needed. +1 Thanks for pointing me in the right direction.Spina
@David Murdoch, Where did you assign the MimeFormatConverter to the MimeFormat property? Also the property is read-only.Litigate
I didn't. I'm posting an answer now so you can see how it was done.Spina
S
1

I figured it out. Here is the solution:

public class MimeFormatElement: ConfigurationElement
{
    #region Constructors
    /// <summary>
    /// Predefines the valid properties and prepares
    /// the property collection.
    /// </summary>
    static MimeFormatElement()
    {
        // Predefine properties here
        _mimeFormat = new ConfigurationProperty(
            "mimeFormat",
            typeof(MimeFormat),
            "*/*",
            ConfigurationPropertyOptions.IsRequired
        );

        _properties = new ConfigurationPropertyCollection {
            _mimeFormat, _enabled
        };
    }
    private static ConfigurationProperty _mimeFormat;
    private static ConfigurationPropertyCollection _properties;

    [ConfigurationProperty("mimeFormat", IsRequired = true)]
    public MimeFormat MimeFormat
    {
        get { return (MimeFormat)base[_mimeFormat]; }
    }
}

/*******************************************/
[TypeConverter(typeof(MimeFormatConverter))]
/*******************************************/
public class MimeFormat
{
    public string Format
    {
        get
        {
            return Type + "/" + SubType;
        }
    }
    public string Type;
    public string SubType;

    public MimeFormat(string mimeFormatStr)
    {
        var parts = mimeFormatStr.Split('/');
        if (parts.Length != 2)
        {
            throw new Exception("Invalid MimeFormat");
        }

        Type = parts[0];
        SubType = parts[1];
    }
}

public class MimeFormatConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return new MimeFormat((string)value);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string);
    }
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        var val = (MimeFormat)value;
        return val.Type + "/" + val.SubType;
    }
}
Spina answered 9/5, 2011 at 18:14 Comment(1)
You don't need to get the TypeConverter manually. Delete TypeDescriptor.GetConverter.... As you did, assiging the TypeConverter to the property or type itself is enough.Litigate
V
0

From this point, you have to create the convert sections within the ConvertTo and ConvertFrom methods

public override object ConvertFrom( ITypeDescriptorContext context, CultureInfo culture, object value ) {
  if ( value == null )
    return null;

  try {
    if ( value is string ) {
      string s = (string)value;

      // here is where you look at the string to figure out the MimeFormat
      // like so....
      return new MimeFormat( s );
    }


  throw new NotSupportedException( NotSupportedException( value.GetType(), typeof(MimeFormat) );
}

public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType ) {
  if ( value == null )
    return null;

  MimeFormat p = (MimeFormat)value;
  if ( destinationType == typeof( String ) )
    return p.ToString();

  throw new NotSupportedException( NotSupportedException( typeof(MimeFormat), destinationType ) );
}

EDITED

You also need to override the CanConvert functions as well.

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

public override bool CanConvertTo( ITypeDescriptorContext context, Type destinationType ) {
  if ( destinationType == typeof( string ) )
    return true;
  return false;
}
Vivisection answered 9/5, 2011 at 16:59 Comment(1)
doesn't work. I still get the error message: The value of the property 'mimeFormat' cannot be parsed. The error is: TypeConverter cannot convert from System.String.Spina

© 2022 - 2024 — McMap. All rights reserved.