DialogPage - string array not persisted
Asked Answered
C

2

8

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?

Comradery answered 18/6, 2014 at 17:0 Comment(4)
Have you tried a List<string> rather than string[]?Argive
When you use List<string> you have to provide a class which has an empty constructor, which string does not. But I even tried to use List<MyCustomClass> where MyCustomClass is a wrapperclass for string- but stil the values won't persist.Jeaninejeanlouis
Did you try StringCollection?Estaminet
@Estaminet I tried with StringCollection and it doesn't get persisted :(Cons
S
6

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.

Seedman answered 29/6, 2014 at 5:3 Comment(1)
Thanks. I wrote my own converter and it works (see my answer).Jeaninejeanlouis
C
6

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);
    }
}
Comradery answered 30/6, 2014 at 9:47 Comment(1)
This solution doesn't work for me. I run into the exact same problems that are describes in this thread #32751540Downgrade

© 2022 - 2024 — McMap. All rights reserved.