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?
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.
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);
}
}