Laden...

Serialisierung von ListView

Erstellt von kstanger vor einem Jahr Letzter Beitrag vor einem Jahr 342 Views
K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr
Serialisierung von ListView

Hallo allerseits,

ich möchte ein ListView deserialisieren und auch wieder serialisieren. Bisher fällt mir nichts anderes ein, als die Items und Subitems in einer Laufschleife zu durchlaufen und die Deserialisierung nach Json durch eigenen Code durchzuführen. Es gibt die Methode ListViewItem.Deserialize(SerializationInfo, StreamingContext), aber damit komme ich irgendwie nicht klar.
Ein passendes Beispiel habe ich auch nicht gefunden.
Kann mir jemand helfen?

Karl Stanger
16.806 Beiträge seit 2008
vor einem Jahr

Man serialisiert keine UI Elemente, sondern Datenklassen.

K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr

Aber es muss doch eine Datenklasse hinter ListView stecken - ich denke, dass es eine List ist. Aber wie hießt die und wie greife ich darauf zu?

Karl Stanger
16.806 Beiträge seit 2008
vor einem Jahr

Nein, das ist nicht die Idee hinter Serialisierung eigener Daten.
Ich dachte, dass Du Dir die Zeit genommen hättest die Links zu lesen, die ich/andere Dir in den anderen Themen gegeben haben. Deswegen antworten Dir die Leute mit den Links 😉

Punkt 1: man serialisiert keine Elemente einer UI.
Dass sowas in manchen UI-Elementen in WinForms enthalten war (noch ist), war damals ein pragmatischer Weg, den man aber bereut.
Das Zeug ist nur noch aus Kompatibilität drin - soll man nicht mehr verwenden (was auch in den Docs steht).

Punkt 2: verwendet man es doch, dann ist die Folge, dass Du eine Abhängigkeit an ein fremdes Element hast.
Das heisst: ändert sich was in/unter der ListView, dann funktioniert Deine gesamte Serialisierung nicht mehr.
Das ist eine Abhängigkeit, die man aus Konzeptgründen nicht haben will.
[Artikel] Drei-Schichten-Architektur
Die Grundidee hier ist, dass Dein gesamter Code auch funktionieren soll, wenn Du eine andere UI Technologie verwendest.
Daher haben modernere UI Frameworks auch keinerlei Serialisierungsunterstützung.

Daher ist die Lösung: eigene Klassen bauen für alles, was man selbst serialisiert / in der Verantwortung hat.
Und ja das heisst: mehr Code, beim Applikatiosstart Zeugs lesen, deserialisieren und damit die Liste füllen bzw. umgekehrt fürs Speichern.

K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr

Ich hatte es so gemacht, wie du gesagt hast, aber:
Mehrdimensionale Arrays können während Runtime nicht vergrößert werden.


                string jsonString = File.ReadAllText(TextBoxPath.Text + TextBoxRGB.Text);
                Farben = JsonSerializer.Deserialize<Farbe[]>(jsonString);
                for (int loop = 0; loop < Farben.Length; loop++)
                {
                    ListViewItem FarbeNr = ListViewRGB.Items.Add(Convert.ToString(Farben[loop].FarbeNr));
                    FarbeNr.SubItems.Add(Convert.ToString(Farben[loop].FarbeRot));
                    FarbeNr.SubItems.Add(Convert.ToString(Farben[loop].FarbeGruen));
                    FarbeNr.SubItems.Add(Convert.ToString(Farben[loop].FarbeBlau));
                    FarbeNr.SubItems.Add(Farben[loop].FarbeBeschreibung);
                }


Wenn der User eine neue Zeile im ListView erzeugt (also z.B. eine neue Farbe gelb einfügt), kann ich das nicht mehr in Farben kopieren, bzw. Farben nicht mehr erweitern. Also brauche ich etwas anderes. Letztendlich stellt sich die Frage, wie ich dieses User-Verhalten abbilden kann?

Karl Stanger
16.806 Beiträge seit 2008
vor einem Jahr

Arrays können generell nie erweitert werden, sondern müssen mit einer gewisse Größe erzeugt werden.
Ist das zu klein, muss ein neues Array erzeugt und die Inhalte übertragen werden.

Deswegen gibt es in .NET Listen, die einem sowas abnehmen.
List<T> Klasse (System.Collections.Generic)

K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr

Und ich dachte, dass hinter dem UI-Element ListView eine solche Liste steckt, die man nur richtig ansprechen muss.
Also muss ich jetzt statt meines Arrays eine Liste erzeugen, und die Daten vom UI-Element dort speichern.

Karl Stanger
16.806 Beiträge seit 2008
vor einem Jahr

Ja, dahinter steckt eine Art von Collection (ob Liste oder Array spielt keine Rolle).
Aber man verwendet nicht die UI-Klassen der Liste für die Serialisierung - sondern eigene.

Also muss ich jetzt statt meines Arrays eine Liste erzeugen, und die Daten vom UI-Element dort speichern.

Ne, hab ich auch nicht geschrieben.
Hab geschrieben: eigene Klasse für die Daten.
Ob das in einem Array oder in einer Liste gespeichert wird, ist für die Serialisierung völlig egal.

Die Liste hab ich nur ins Spiel gebracht weil Du geschrieben hast

Mehrdimensionale Arrays können während Runtime nicht vergrößert werden.

Der Kernpunkt ist immer noch meine Aussage: eigene Klasse für jegliches Speichern von Inhalten.
Man serialisiert keine UI-Klassen/UI-Objekte.

K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr

Ja, alles ok und akzeptiert. Aber ich bin damit leider nicht weiter.
Nochmal: ich habe ein ListView, in das ich (wie auch immer) 4 Farben eingetragen habe mit jeweils Nr, R-Wert, G-Wert, B-Wert, Beschreibung. Diese Daten habe (wie auch immer) als Json in eine Datei geschrieben. Nun fülle ich eine 5. Farbe in mein ListView und will alles nochmal als Json in der Datei haben.
Was muss ich tun? Wie muss mein Konzept für die Datenspeicherung aussehen?

Karl Stanger
16.806 Beiträge seit 2008
vor einem Jahr

Ein Tipp der hilft, den man auch in Schulungen macht: wenn Du den eigenen Code und Flow nicht verstehst, dann mal ihn wie ein Diagramm auf ein Papier.
Und wenn Du merkst, dass der Flow funktioniert, dann schreib ihn in Code.

Im Endeffekt, mit der Annahme, dass Farbe Deine Datenklasse ist:
Lesen:

  • Farben lesen
  • Farbe für Farbe in die ListView eintragen

Schreiben:

  • ListView Elemente nehmen, durch Items.Count weißt Du wie viele Elemente in der Liste sind und kannst mit dieser Info das Array initialisieren
  • Jeden Eintrag in Farbe übertragen
  • Farbe den Array Farben hinzufügen
  • Farben schreiben

Es ist schwer zu verstehen, wo genau Dein Problem ist.
Ich will Dir aber nicht den Code schreiben, Du sollst es ja verstehen, sodass es Ping macht.

K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr

Ich versuche, jetzt Schritt für Schritt deinen Vorschlägen zu folgen.

Annahme, dass Farbe Deine Datenklasse ist


    [Serializable]
    public class Farbe
    {
        public byte FarbeNr { get; set; }
        public byte FarbeRot { get; set; }
        public byte FarbeGruen { get; set; }
        public byte FarbeBlau { get; set; }
        public string FarbeBeschreibung { get; set; }

        public Farbe(byte farbeNr, byte farbeRot, byte farbeGruen, byte farbeBlau, string farbeBeschreibung)
        {
            FarbeNr = farbeNr;
            FarbeRot = farbeRot;
            FarbeGruen = farbeGruen;
            FarbeBlau = farbeBlau;
            FarbeBeschreibung = farbeBeschreibung;
        }
    }


  • Farben lesen
  • Farbe für Farbe in die ListView eintragen

        Farbe[] Farben;
        private void ButtonRGBLesen_Click(object sender, EventArgs e)
        {
            if (File.Exists(TextBoxPath.Text + TextBoxRGB.Text))
            {
                string jsonString = File.ReadAllText(TextBoxPath.Text + TextBoxRGB.Text);
                Farben = JsonSerializer.Deserialize<Farbe[]>(jsonString);
                for (int loop = 0; loop < Farben.Length; loop++)
                {
                    ListViewItem FarbeNr = ListViewRGB.Items.Add(Convert.ToString(Farben[loop].FarbeNr));
                    FarbeNr.SubItems.Add(Convert.ToString(Farben[loop].FarbeRot));
                    FarbeNr.SubItems.Add(Convert.ToString(Farben[loop].FarbeGruen));
                    FarbeNr.SubItems.Add(Convert.ToString(Farben[loop].FarbeBlau));
                    FarbeNr.SubItems.Add(Farben[loop].FarbeBeschreibung);
                }
            }
            else
            {
                MessageBox.Show(TextBoxPath.Text + TextBoxRGB.Text + " nicht gefunden");
            }
        }


  • ListView Elemente nehmen, durch Items.Count weißt Du wie viele Elemente in der Liste sind und kannst mit dieser Info das Array initialisieren

Im Eventhandler mache ich dann folgendes:


           Farbe[] Farben = new Farbe[ListViewRGB.Items.Count];

Ich meine, dass ich dadurch ein lokales Array mit einer festen Größe definiert habe.
Und dann habe ich ein Problem:

  • Jeden Eintrag in Farbe übertragen

Wie kann ich einen Eintrag der in die Klasse Farbe übertragen? Wenn ich


Farben[loop].FarbeNr = Convert.ToByte(ListViewRGB.Items[loop].Text);

versuche, bekomme ich die Exception
System.NullReferenceException: "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."
Ich dachte, Farben sei die Instanz?

Karl Stanger
16.806 Beiträge seit 2008
vor einem Jahr

Ich meine, dass ich dadurch ein lokales Array mit einer festen Größe definiert habe.

Arrays haben immer eine feste Größe.

Ich dachte, Farben sei die Instanz?

Ja, Du hast das Array Farben erzeugt. Das ist dann "leer".
Mit Farben[loop] versuchst Du aber ein Objekt Farbe zu verwenden, das nicht erzeugt wurde.


for index in listitems
   Farbe farbe = new Farbe(mit Deinen Werten)
   farben[index] = farbe

Das siehst Du auch im Debugger, mit denen sich solche Fehler sehr einfach finden lassen.
[Artikel] Debugger: Wie verwende ich den von Visual Studio?

K
kstanger Themenstarter:in
99 Beiträge seit 2022
vor einem Jahr

Ich merke, dass es bei mir mit der Objektorientierung noch ziemlich hapert.
Jetzt funktioniert es jedenfalls und für heute reicht es auch 😉

Karl Stanger
M
368 Beiträge seit 2006
vor einem Jahr

Objektorientierung ...da gehört mehr dazu als "nur" ein (oder mehrere) Instanzobjekt(e) anzulegen: Rheinwerk - Openbook OOP und Rheinwerk - Openbook VC# '12 (Kap. 3)

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉