2015-09-23 27 views
7

Angenommen, ich muss jedes Array in der Erweiterung speichern gerade neu aus der Vorlage erstellt.Speichern Sie Array in Optionen mit DialogPage

Ich habe gerade neue VSIX Projekt hinzugefügt VSPackage es, hinzugefügt dann Option Seitenraster (DialogPage). Dann folgte ich Anweisungen von Antworten auf eine ähnliche Frage: DialogPage - string array not persisted.

Und zu Demonstrationszwecken, lassen Sie sich auch int[] Array und schlicht int mit benutzerdefinierten Wandler hinzufügen.

// [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, '*'); 
    } 
} 

Wenn ich diese Optionen bearbeiten, kann ich sehen, dass der Konverter wirklich funktioniert: Type Converter really works

Aber nachdem ich es wieder öffnen, zwei der Werte weg! Nur Normal int beharrte: Array values are lost, but plain int persisted

Es gibt auch eine seltsame Sache: wie und wann TypeConverter Methoden aufgerufen werden. CanConvertTo() ist nie während der gesamten Sitzung aufgerufen. CanConvertFrom() und ConvertTo() werden oft und mehr oder weniger in erwarteter Weise genannt. Und ConvertFrom() heißt nur, wenn die Zeichenfolgendarstellung der Option direkt bearbeitet wird, d. H. Es nicht teilnehmen in Laden/Speichern Optionen überhaupt!

Ich bin nicht sicher, aber es fühlt sich ein bisschen wie int Option wird als int gespeichert und wandte sich aus/in string nur in Optionen GUI, während Array Optionen leise nur versucht, nicht das gleiche zu tun.

PS: Wenn Sie direkt spielen mit dem Beispiel persönlich wollen, hier ist ein GitHub Repo mit dem Beispielprojekt in Frage: FooBarVSIXProject

Antwort

7

Nach mehreren Stunden damit verbringen, Mechanismus zu reparieren gebrochen „easy to use“ (entweder selbst ist kaputt oder seine Dokumentation), ich erkannte, dass, anstatt Zeit zu verschwenden, hätte ich nur eine Abstraktionsschicht nach unten und tun genau das, was ich wollte DialogPage Mechanismus automatisch tun.

Man würde erwarten, dass DialogPage sollte die Stringdarstellung (erhalten durch Typ-Wandler) in/aus User Settings Store (oder so ähnlich) speichern/laden, wenn seine SaveSettingsToStorage() und LoadSettingsFromStorage() genannt werden. Da es dies ab und diese Methoden sind virtual, können wir genau das tun sie:

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 
    } 
} 

Nun, natürlich, wenn Sie es auf diese Weise tun, müssen Sie nicht schreiben und Verwendung TypeConverter, eigentlich. Sie können die Serialisierungslogik einfach in diese Methoden oder überall einbetten.

Sie können Ihre Daten auch direkt in Binärformat serialisieren und SetMemoryStream() verwenden, um es zu speichern.

+2

Dies ist ein Fehler in VS 2015 MS im Wesentlichen die Logik in DialogPage.LoadSettingsFromStorage und SaveSettingsToStorage zwischen VS 2013 und VS 2015 geändert, und sie brach LoadSettingsFromStorage für Eigenschaften, die Typeconverter verwenden. Ich habe dies über den "Problem melden" -Dialog von VS 2015 und über Connect gemeldet, also werden sie es eventuell beheben. In der Zwischenzeit musste ich auch mit Overrides umgehen, wie du es getan hast. Hinweis: TypeConverter sind weiterhin nützlich, um Werte im PropertyGrid zu bearbeiten. Außerdem befindet sich der MS-Fehler in DialogPage.SetPropertyValue, der Convert.ChangeType jetzt aufruft. –