Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DialogPage - string array not persisted

I'm developing an extension for visual studio.

There I have an option page:

public class GeneralOptionsPage : DialogPage
{
    [Category("General")]
    [DisplayName("Foos")]
    [Description("Bla Foo Bla")]
    public string[] Foos { get; set; }


    [Category("General")]
    [DisplayName("Bar")]
    [Description("Bar Foo Bar")]
    public string Bar { get; set; }
}

The Bar property works perfectly and is persisted.

The Foos Property does also work (it even gives you a nice popup in the options page where you can enter one string per line), which means I can set it and also use it in my extension but it is not written to the registry/storage. When I close VS and open it again it's always empty.

Quote from MSDN:

The default implementation of DialogPage supports properties that have appropriate converters or that are structures or arrays that can be expanded into properties that have appropriate converters. For a list of converters, see the System.ComponentModel namespace. The Visual Studio Extensibility Samples manages int, string, and System.Drawing.Size properties.

In my understanding I'm using valid components from the System.ComponentModel namespace.

So what am I doing wrong? Do I have to treat arrays somehow differently?

like image 900
OschtärEi Avatar asked Jun 18 '14 17:06

OschtärEi


2 Answers

You will need to implement and associate a custom TypeConverter for your Foos property.

There is no stock converter that covers this scenario, because when you are converting an array of strings to a string, you have to have some sort of delimiter, so you can reconstitute the array from the string. And that will vary depending upon the individual programmers application. Hence the need for a custom TypeConverter.

So you have to derive a new class from System.ComponentModel.TypeConverter, and then associate it with your Foos property. For example:

    [Category("General")]
    [DisplayName("Foos")]
    [Description("Bla Foo Bla")]
    [TypeConverter(typeof(FoosCoverter))]
    public string[] Foos { get; set; }

A quick internet search should pull up a few examples to get you pointed in the right direction.

The DialogPage.SaveSettingsToStorage loops through a collection of the page's PropertyDescriptors, retrieves each propertie's Converter, invokes CanConvertTo and CanConvertFrom to ensure the property can be converted to/from a string, then calls the converter's ConvertToInvariantString to persist the property to the registry value.

Conversely, the DialogPage.LoadSettingsfromStorage loops through the registry values, finds the PropertyDescriptor matching the property on the DialogPage, retrieves its Coverter, then calls CanCovertFrom to ensure the string can be converted back to that particular property type, and then calls the converter's ConvertFromIvariantString, and then assigns the value back to the property.

like image 84
Ed Dore Avatar answered Nov 08 '22 00:11

Ed Dore


Ed Dore provied the correct answer (thanks).

As an example here my custom TypeConverter:

class StringArrayConverter : TypeConverter
{
    private const string delimiter = "#@#";

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

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string[]) || base.CanConvertTo(context, destinationType);
    }

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

        return v == null ? base.ConvertFrom(context,culture,value) : v.Split(new[] {delimiter}, StringSplitOptions.RemoveEmptyEntries);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        string[] v = value as string[];
        if (destinationType != typeof(string) || v == null)
        {
            return base.ConvertTo(context, culture, value,destinationType);
        }
        return string.Join(delimiter, v);
    }
}
like image 6
OschtärEi Avatar answered Nov 07 '22 23:11

OschtärEi