Properties eines eigenen Controls in Datei Speichern/Laden

  • C#

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von simpelSoft.

    Properties eines eigenen Controls in Datei Speichern/Laden

    Hallo zusammen,

    dieses Thema ist für mich Neuland - ich möchte alle Properties eines eigenen Controls zur Weitergabe an andere Benutzer in eine Datei speichern und wieder laden.
    Die Properties bestehen nur aus den Typen bool, int und Color - also recht überschaubar, dafür aber in großer Menge.
    Als Datei dachte ich an XML oder JSON, bin aber für alle anderen Vorschläge sehr offen.
    Ein anderer Benutzer soll diese Datei öffnen können und das Control soll dann diese Einstellungen übernehmen.

    Wie geht man am besten an dieses Thema heran?
    Im Web findet man Unmengen Ansätze - gibt es da eine best practice?
    Um so einfacher, um so lieber.

    Danke vorab für den Schubs in die richtige Richtung!

    PS: Vor langer Zeit hatte EDR mal einen Serializer vorgestellt (finde ihn momentan nicht wieder), der konnte so gut wie alles serialisieren bis auf Bierdeckel :D .
    Vielleicht gehts ja noch einfacher?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „simpelSoft“ ()

    nein, es geht nicht einfacher. Es ist auch kein Serialisierer.
    Das Ding ist im SourceCodeaustausch und heisst "ComplexConverter"

    Es ist auch nicht "bestPractice", weil dafür fehlt ihm die nötige Bekanntheit.
    Üblicherweise (also sog. "bestPractice") würde man sowas mit Serialisierung abfackeln - da müsstest du
    • eine Serialisierbare Klasse schreiben, die alle Daten abbildet, die du persistieren willst
    • einen Control-Ausleser schreiben, der das Control ausliesst und ein Objekt deiner Klasse aufbaut
    • einen Klassen-Ausleser schreiben, der - nach Deserialisierung - deine Klasse ausliest, und die Daten auf dein Control anwendet.
    Beim ControlConverter musst du auch was schreiben
    • zunächstmal kapieren wies tickt, und das Event richtig abonnieren
    • Im Event eine Art Control-Ausleser schreiben, der alles benennt, was du persistieren willst
    Dassis deutlich weniger Arbeit und ist flexibler - etwa wenn du noch Änderungen am Control vornimmst, oder an dem, was du daran persistieren willst.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @simpelSoft Warum serialisierst Du diese Klasse nicht einfach direkt per XML?
    Da sollte es keine Schwierigkeiten geben.
    Binär könnte es krachen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hab's nicht probiert, aber die gewünschten Properties mit nem Attribut markieren und per Reflection diese in ne Datei hauen. Und beim Laden eben wieder die Properties erfassen und mit Daten befüllen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Schnell mal was zusammengeschustert, zumindest für's Speichern:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim Foo As New Foo With {.Bar = 42, .Bazz = "Bazz", .Buzz = 42.2}
    3. Dim RelevantProperties = GetType(Foo).GetProperties().Where(Function(x) Attribute.IsDefined(x, GetType(SaveInFileAttribute)))
    4. Dim ValuesToSaveInFile = RelevantProperties.Select(Function(x) x.GetValue(Foo))
    5. 'und hier dann die Werte von ValuesToSaveInFile in die Datei schieben
    6. End Sub
    7. '…
    8. Public Class Foo
    9. <SaveInFile>
    10. Property Bar As Integer
    11. Property Bazz As String
    12. <SaveInFile>
    13. Property Buzz As Double
    14. End Class
    15. <System.AttributeUsage(System.AttributeTargets.Property)>
    16. Public Class SaveInFileAttribute : Inherits System.Attribute
    17. End Class

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    simpelSoft schrieb:

    ...mit nem Attribut markieren und per Reflection diese in ne Datei hauen.

    Ja, darauf wird es hinauslaufen, danke.
    Echt unumgänglich?

    ich rufe nochmal post#2 in Erinnerung, und meine wagemutige Behauptung, das sei das einfachst-mögliche.
    Das mit den Attributen ist auch nett und sehr generisch, könnte allerdings problematisch werden, wenn du von deim Control auch Properties saven willst, die du nicht selbst entwickelt hast.
    Attributieren kannste ja nur Properties, die du geschrieben hast.
    Klar kannst du alle diese Properties dann überschreiben oder abschattieren - aber das sind dann 9 Zeilen pro Property.
    Beim ComplexConverter schreibst du für jeden zu persistierenden Wert genau eine Zeile (und für Listen eine Schleife).
    Ich glaub tatsächlich, es geht nicht einfacher. Ob man nun ein Attribut setzt, oder den CC-Befehl hinschreibt - mindestens eine Zeile muss man aufwenden.

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Attributieren kannste ja nur Properties, die du geschrieben hast.
    Du kannst doch die Property überschreiben.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    ErfinderDesRades schrieb:

    In c# funktioniert ComplexConverter nicht.
    Ich hab mir mal Deinen ComplexConverter (VS2005) runtergeladen und eben fix nach C# konvertiert, ich poste ihn hier:
    ComplexConverter: alles in einen String und zurück
    Der 2013-Code zeigt keinen TreeView und keine RTB an.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Neu

    VaporiZed schrieb:

    Schnell mal was zusammengeschustert, zumindest für's Speichern

    Hallo,
    danke dafür!
    Ich muss feststellen, mir fällt es immer schwerer, mich in Themen einzuarbeiten, mit denen ich mich nie beschäftigt habe, aber was solls.
    Also Dein Ansatz ist hervorragend, finde ich.
    Die Attribute in den Properties funktionieren wie gewünscht, den Zusammenhang habe ich kapiert.
    Weiterhin habe ich verstanden, dass Du zwei Collections generierst. Einmal die Propertyinfos aller mit den Attributen versehenen Properties relevantProperties und dann eine Collection der Values der Properties valuesToSaveInFile.
    Zum Test habe ich mal die erste Collection durchlaufen und dann separat die Values gefischt, Propertyname und Value kommen korrekt.
    Zum Speichern in der Datei sollten dann m.M. nach die Propertynamen und Values rein, nicht nur die Values.
    Vielleicht müsste man auch die komplette Propertyinfo serialisieren? Denn woher will man beim Einlesen die Typen unterscheiden (der ValueConverter hat auch sein Grenzen).

    Spoiler anzeigen

    C#-Quellcode

    1. private void ButtonStoreModuleSettings_Click(object sender, EventArgs e) {
    2. var relevantProperties = typeof(MetersFFT).GetProperties().Where((x) => Attribute.IsDefined(x, typeof(SaveInFileAttribute)));
    3. //var valuesToSaveInFile = relevantProperties.Select((x) => x.GetValue(metersFFT1));
    4. foreach (var item in relevantProperties) {
    5. var val = metersFFT1.GetType().GetProperty(item.Name).GetValue(metersFFT1, null);
    6. WriteControllerLog(String.Format("{0}, {1}", item.Name, val), MsgType.info);
    7. }
    8. }


    Leider bin ich weit davon entfernt, aus den Collections ein .XML oder JSON Konstrukt zu generieren, ich gebe zu, habe da wirklich Verständnisprobleme.
    Ich weiß auch nicht, ob ich jetzt weitermache, denn es ist nur ein Konfigurationsprogramm für ein Mikrocontrollerprojekt und das Speichern und Laden der Einstellungen könnte ich auch später noch implementieren.
    Sozusagen wenn der Kopf mal wieder frei und Aufnahmefähig ist.
    Ich hätte nicht gedacht, dass das Speichern und Laden von Einstellungen doch so kompliziert ist.

    So sieht das Konfigurationsprogramm dann aus:

    Neu

    Ist vielleicht für den Anfang ausreichend, wenn Du nicht nur die Werte, sondern tatsächlich den Namen der Property mit dazuschreibst. Aber dann wird's insofern natürlich tricky, wenn Du ein eigenständiges Dateiformat nimmst.
    Sowas ist mir zuerst in den Kopf gekommen:
    PropertyName-PropertyValue
    oder
    PropertyName:PropertyValue
    oder

    Quellcode

    1. PropertyName
    2. PropertyValue

    Aber dann kann Dir immer der Value dazwischenfunken, wenn da das von Dir gewählte Trennzeichen zwischen PropertyName und P-Value drinsteckt.
    Dann alternativ diese Werte in ne eigene Klasse schreiben und diese bei JSON/XML serialisieren. Da werden solche Späße ordentlich behandelt. Und dann über den gleichen Weg zurück in die CustomClass deserialisieren und von dort wieder in Deine Ursprungsklasse. Musste mal ausprobieren, ob Variante 1 vielleicht bei Deinen (v.a. String-Properties) gangbar ist.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Neu

    Hat mir doch keine Ruhe gelassen - habe fertig :) .
    Serialisiert wird binär, dann gibts auch keine Probleme oder nötige Zwischenlösungen mit Properties wie z.B. "Color".
    Vielleicht noch nicht ganz hübsch, aber recht kurz und funktioniert bei mir einwandfrei.
    Alle Properties werden brav gespeichert und wieder gesetzt.
    File ist noch hardcoded, wird natürlich geändert, jetzt erst mal schlafen.
    Vielleicht kann es ja Jemand gebrauchen ...

    C#-Quellcode

    1. [Serializable]
    2. public class AppSettings {
    3. public string name { get; set; }
    4. public object val { get; set; }
    5. }
    6. private void ButtonLoadFFTSettingsFromFile_Click(object sender, EventArgs e) {
    7. List<AppSettings> settingsFFT = (List<AppSettings>)DeSerializeList(@"d:\ModuleSettingsFFT.bin");
    8. foreach (var item in settingsFFT) {
    9. var propInfo = metersFFT1.GetType().GetProperty(item.name);
    10. if (propInfo != null) propInfo.SetValue(metersFFT1, item.val, null);
    11. }
    12. }
    13. private void ButtonSaveFFTSettingsToFile_Click(object sender, EventArgs e) {
    14. var relevantProperties = typeof(MetersFFT).GetProperties().Where((x) => Attribute.IsDefined(x, typeof(SaveInFileAttribute)));
    15. List<AppSettings> settingsFFT = new List<AppSettings>();
    16. foreach (var item in relevantProperties) {
    17. settingsFFT.Add(new AppSettings() {
    18. name = item.Name,
    19. val = item.GetValue(metersFFT1, null)
    20. });
    21. }
    22. SerializeList(settingsFFT, @"d:\ModuleSettingsFFT.bin");
    23. }
    24. private void SerializeList(List<AppSettings> targets, string targetPath) {
    25. IFormatter formatter = new BinaryFormatter();
    26. using (FileStream stream = File.Create(targetPath))
    27. formatter.Serialize(stream, targets);
    28. }
    29. private object DeSerializeList(string sourcePath) {
    30. IFormatter formatter = new BinaryFormatter();
    31. using (FileStream stream = File.OpenRead(sourcePath)) {
    32. stream.Seek(0, SeekOrigin.Begin);
    33. return formatter.Deserialize(stream);
    34. }
    35. }

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „simpelSoft“ ()

    Neu

    Guten Morgen,
    gehts noch simpler? Klaro!
    Da wir ja nur zwei Werte speichern wollen (Name der Property und deren Inhalt als Objekt), brauchen wir die List of class nicht mal, ein Dictionary reicht da völlig:

    C#-Quellcode

    1. private void ButtonLoadFFTSettingsFromFile_Click(object sender, EventArgs e) {
    2. Dictionary<string, object> settingsFFT = (Dictionary<string, object>)DeSerializeProps(@"d:\ModuleSettingsFFT.bin");
    3. foreach (var item in settingsFFT) {
    4. var propInfo = metersFFT1.GetType().GetProperty(item.Key);
    5. if (propInfo != null) propInfo.SetValue(metersFFT1, item.Value, null);
    6. }
    7. }
    8. private void ButtonSaveFFTSettingsToFile_Click(object sender, EventArgs e) {
    9. var relevantProperties = typeof(MetersFFT).GetProperties().Where((x) => Attribute.IsDefined(x, typeof(SaveInFileAttribute)));
    10. Dictionary<string, object> dict = new Dictionary<string, object>();
    11. foreach (var item in relevantProperties) {
    12. dict.Add(item.Name, item.GetValue(metersFFT1, null));
    13. }
    14. SerializeProps(dict, @"d:\ModuleSettingsFFT.bin");
    15. }
    16. private void SerializeProps(Dictionary<string, object> targets, string targetPath) {
    17. IFormatter formatter = new BinaryFormatter();
    18. using (FileStream stream = File.Create(targetPath))
    19. formatter.Serialize(stream, targets);
    20. }
    21. private object DeSerializeProps(string sourcePath) {
    22. IFormatter formatter = new BinaryFormatter();
    23. using (FileStream stream = File.OpenRead(sourcePath)) {
    24. stream.Seek(0, SeekOrigin.Begin);
    25. return formatter.Deserialize(stream);
    26. }
    27. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „simpelSoft“ ()