使用 DialogPage 将数组存储在选项中

Store array in options using DialogPage

假设我需要将任何数组存储在刚刚从模板创建的扩展中。

我刚刚创建了新的 VSIX 项目,添加了 VSPackage,然后添加了 option page gridDialogPage)。然后我按照类似问题的答案中的说明进行操作:DialogPage - string array not persisted.

并且,为了演示目的,我们还添加 int[] 数组和带有自定义 类型转换器 .

的普通 int
// [standard attributes]
[ProvideOptionPage(typeof(OptionPageGrid),
"My Category", "My Grid Page", 0, 0, true)]
public sealed class FooBarVSPackage : Package
{
    // standard code
}

public class OptionPageGrid : DialogPage
{
    // [typical attributes]
    [TypeConverter(typeof(StringArrayConverter))]
    public string[] Foos
    { get; set; }

    // [typical attributes]
    [TypeConverter(typeof(CustomIntConverter))]
    public int Bar
    { get; set; }

    // [typical attributes]
    [TypeConverter(typeof(IntArrayConverter))]
    public int[] Bazes
    { get; set; }
}

class StringArrayConverter : TypeConverter
{
    // exact copy of code from similar question/answer mentioned above
}

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

    // CanConvertFrom, ConvertTo, etc. overridden in similar fashion
}

public class CustomIntConverter : TypeConverter
{
    // CanConvertFrom() overridden
    // CanConvertTo() overridden

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var v = value as string;
        return int.Parse(v.TrimStart('*'));
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        var v = (int)value;
        return v.ToString().PadLeft(25, '*');
    }
}

当我编辑这些选项时,我可以看到转换器确实有效:

但是我重新打开后,有两个值没有了!只有普通 int 坚持:

还有一件奇怪的事情:如何以及何时调用 TypeConverter 方法。 CanConvertTo() 从未 在整个会话期间被调用。 CanConvertFrom()ConvertTo() 经常或多或少以预期的方式被调用。而ConvertFrom()只有在直接编辑选项的字符串表示时才会调用,即参与loading/saving 选项!

我不确定,但感觉有点像 int 选项存储为 int 并仅在选项 GUI 中变成 from/into string,而 array 选项只是默默地失败尝试做同样的事情。

P.S.: 如果你想直接亲自玩这个例子,这里有一个 GitHub repo 有问题的例子项目:FooBarVSIXProject

在花了几个小时试图修复损坏的“易于使用”机制(本身损坏或它的文档)之后,我意识到与其浪费时间,我应该只下降一个抽象层并做正确的事情我想要 DialogPage 机制自动执行。

人们会期望 DialogPage 应该 save/load 调用字符串表示形式(通过类型转换器获得)into/from User Settings Store (or something like that) when its SaveSettingsToStorage() and LoadSettingsFromStorage()。由于它拒绝这样做并且这些方法是 virtual,我们可以自己做:

public class OptionPageGrid : DialogPage
{
    const string collectionName = "FooBarVSIX";

    [Category("General")]
    [DisplayName("Foos")]
    [Description("Bla Foo Bla")]
    // note that TypeConverter attribute is removed,
    // because it's not relevant anymore
    public string[] Foos
    { get; set; }

    // Bar and Bazes properties missed out to make this example shorter

    public override void SaveSettingsToStorage()
    {
        base.SaveSettingsToStorage();

        var settingsManager = new ShellSettingsManager(ServiceProvider.GlobalProvider);
        var userSettingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings);

        if (!userSettingsStore.CollectionExists(collectionName))
            userSettingsStore.CreateCollection(collectionName);

        var converter = new StringArrayConverter();
        userSettingsStore.SetString(
            collectionName,
            nameof(Foos),
            converter.ConvertTo(this.Foos, typeof(string)) as string);
        // save Bazes in similar way
    }

    public override void LoadSettingsFromStorage()
    {
        base.LoadSettingsFromStorage();

        var settingsManager = new ShellSettingsManager(ServiceProvider.GlobalProvider);
        var userSettingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings);

        if (!userSettingsStore.PropertyExists(collectionName, nameof(Foos)))
            return;

        var converter = new StringArrayConverter();
        this.Foos = converter.ConvertFrom(
            userSettingsStore.GetString(collectionName, nameof(Foos))) as string[];
        // load Bazes in similar way
    }
}

现在,当然,如果您这样做,实际上就不必编写和使用 TypeConverter。您可以将序列化逻辑直接嵌入到这些方法或任何地方。

此外,您可以将数据直接序列化为二进制格式并使用 SetMemoryStream() 保存。