Benutzer mit den meisten Antworten
Elemente in einer Liste speichern, so dass man sie wieder auslesen kann

Frage
-
Hallo zusammen,
Ich habe in der Berufsschule folgende Aufgabe bekommen:
"Erstellen Sie ein Programm welches das Organisieren von Daten ermöglicht."
In diesem Programm gibt es 3 verschiedene Kategorien: Termine, ToDos, Notizen.
Nun muss es möglich sein, ein Element zu erzeugen, welches die 3 Unterelemente Notizen, Termine und ToDos enthält.
Die Daten die erzeugt werden müssen in einer List<Elemente> abgespeichert werden.
"Elemente" ist die Überklasse der 3 Klassen Notizen, Termine und ToDos.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; using System.Xml.Serialization; namespace Organisierer { public partial class Form1 : Form { List<Elemente> Element = new List<Elemente>(); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void btnName_Click(object sender, EventArgs e) { Element.Add(new Notizen(tbNeu.Text, "Notizen")); Element.Add(new Termine(tbNeu.Text, "Termin")); Element.Add(new ToDos(tbNeu.Text, "ToDo")); foreach(Elemente ele in Element) { Console.WriteLine(ele); } lbBox.Items.Add(tbNeu.Text); tbNeu.Text = ""; } private void btnLöschen_Click(object sender, EventArgs e) { Console.WriteLine(lbBox.SelectedItem.ToString() + " gelöscht."); lbBox.Items.Remove(lbBox.SelectedItem); tbNotizen.Text = ""; tbTermine.Text = ""; tbToDos.Text = ""; } private void btnLaden_Click(object sender, EventArgs e) { try { string curr = lbBox.SelectedItem.ToString(); Console.WriteLine("Momentan ist Element:{0} ausgewählt", curr); Schreiben(); } catch (Exception exc) { Console.WriteLine(exc.Message); } } private void lbBox_SelectedIndexChanged(object sender, EventArgs e) { try { string curr = lbBox.SelectedItem.ToString(); Console.WriteLine("Momentan ist Element:{0} ausgewählt", curr); Schreiben(); } catch (Exception exc) { Console.WriteLine(exc.Message); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Speichern(); } private void btnSpeichern_Click(object sender, EventArgs e) { Speichern(); } public void Speichern() { String Note = tbNotizen.Text; String Termin = tbTermine.Text; String ToDo = tbToDos.Text; string vz = @"Dateien\logs.txt"; StreamWriter sw = new StreamWriter(vz); foreach (Elemente ele in Element) { ele.Inhalt(Note); sw.WriteLine(ele._Name); sw.WriteLine(ele._Inhalt); } Console.WriteLine("Wurde nach '{0}' geschrieben.", vz); sw.Flush(); sw.Close(); } public void Lesen() { } public void Schreiben() { foreach (Notizen note in Element) { tbNotizen.Text = note._Inhalt; } foreach (Termine termin in Element) { tbTermine.Text = termin._Inhalt; } foreach (ToDos todo in Element) { tbToDos.Text = todo._Inhalt; } } } }
So sieht der Codeblock für meine Fensterklasse aus.
Mein Problem ist es, dass ich die Daten nicht so in die Liste gespeichert bekomme um sie wieder aus zu lesen.
Antworten
-
Hallo Luca,
als ich mir deinen Post durchgelesen habe, dachte ich vor dem Code noch dass es eine Kalsse Element gibt, die jeweils eine Liste von ToDos, Terminen und Notizen enthalten kann. Wenn ich mir deinen Code ansehe, denke ich aber eher das Element die Elternklasse ist und die anderen 3 davon erben. Stimmt's?
Mein Problem ist es, dass ich die Daten nicht so in die Liste gespeichert bekomme um sie wieder aus zu lesen.
Hier bin ich mir noch nicht ganz sicher was du genau meinst. Meinst du das Speichern in die Datei?
Falls ja, dann gibt es unzählige Möglichkeiten dafür. Das einfachste wäre sicherlich die Liste einfach zu serialisieren, aber ich vermute mal dass das ihr das eher manuell machen sollt um zu lernen was dahinter steckt.
Da du 3 verschiedene Typen in der Liste hast, musst du diese irgendwie in der Datei unterscheidbar machen. Möglich wäre es beispielsweise dass jeder Eintrag genau eine Zeile bekommt und je nach Typ der Klasse ein anderes Zeichen am Zeilenanfang steht.Hierfür kannst du eine abstrakte Methode benutzen, die du in den 3 Ableitungen überschreibst:
//nur wenn die Klasse abstract ist, kann auch eine Methode abstract sein public abstract class Element{ //anderer Code... public abstract string Serialisieren(); } public class Notitz : Element{ //anderer Code... public override string Serialisieren(){ return "N" + this.Inhalt; } }
In der Speichern-Methode kannst du dann einfach die Element-Liste durchlaufen und jeweils die Serialisieren-Methode aufrufen. Das zurück gegebene schriebst du dann einfach jeweils auf eine neue Zeile.
Beim wieder auslesen kannst du nun Zeile für Zeile einlesen und kannst anhand des ersten Zeichens entscheiden, wie du den nachfolgenden Inhalt zu verarbeiten hast.
Das mit dem ersten Zeichen einer Zeile ist nur eine von unzähligen Möglichkeiten. Du könntest auch komplexere Formate wie XML oder JSON dafür benutzen oder das ganze auch binär abspeichern. Aber ich denke das Beispiel zeigt ganz gut wie so etwas funktionieren kann.
PS: Noch 2 kleine Tipps:
- Passe bei der Benennung auf. Deine Liste "Element" impliziert dass es nur ein einzelnes ist. Listen benennt man daher meistens in der Mehrzahl, hier also Elemente bzw. Elements auf Englisch.
- Wenn du mit Streams arbeitest, empfiehlt es sich using-Blöcke einzusetzen. Diese schließen den Stream ggf. automatisch, auch im Fehlerfall. Ein Aufruf von Close ist dann nicht mehr nötig.
Mehr eine Diskussionsfrage ist hier, ob man lieber alles auf englisch benennen sollte. Ich habe es mir recht schnell so angewöhnt, einfach weil auch alles andere auf Englisch ist. Aber auf Deutsch funktioniert das natürlich trotzdem.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 29. November 2016 07:18
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 20. Dezember 2016 09:47
-
Hallo Luca,
public static List<Element> ReadElementsFromFile (string path) { List<Element> elements = new List<Element>(); using (StreamReader sr = new StreamReader(path)) { while (!sr.EndOfStream) { string identifierPlusContent = sr.ReadLine(); Element newElement = StaticElementFactory.GetElementFromString(identifierPlusContent); elements.Add(newElement); } } return elements; }
class StaticElementFactory { public static Element GetElementFromString(string identifierPlusContent) { Element newElement; char identifier = identifierPlusContent.First(); switch (identifier) { case 'N': newElement = new Notiz(); break; case 'T': newElement = new Termin(); break; case 'O': newElement = new ToDo(); break; default: newElement = null; break; } newElement.Content = identifierPlusContent.Remove(0, 1); return newElement; } }
Beste Grüße- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Freitag, 9. Dezember 2016 10:48
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 20. Dezember 2016 09:47
Alle Antworten
-
Hallo Luca,
als ich mir deinen Post durchgelesen habe, dachte ich vor dem Code noch dass es eine Kalsse Element gibt, die jeweils eine Liste von ToDos, Terminen und Notizen enthalten kann. Wenn ich mir deinen Code ansehe, denke ich aber eher das Element die Elternklasse ist und die anderen 3 davon erben. Stimmt's?
Mein Problem ist es, dass ich die Daten nicht so in die Liste gespeichert bekomme um sie wieder aus zu lesen.
Hier bin ich mir noch nicht ganz sicher was du genau meinst. Meinst du das Speichern in die Datei?
Falls ja, dann gibt es unzählige Möglichkeiten dafür. Das einfachste wäre sicherlich die Liste einfach zu serialisieren, aber ich vermute mal dass das ihr das eher manuell machen sollt um zu lernen was dahinter steckt.
Da du 3 verschiedene Typen in der Liste hast, musst du diese irgendwie in der Datei unterscheidbar machen. Möglich wäre es beispielsweise dass jeder Eintrag genau eine Zeile bekommt und je nach Typ der Klasse ein anderes Zeichen am Zeilenanfang steht.Hierfür kannst du eine abstrakte Methode benutzen, die du in den 3 Ableitungen überschreibst:
//nur wenn die Klasse abstract ist, kann auch eine Methode abstract sein public abstract class Element{ //anderer Code... public abstract string Serialisieren(); } public class Notitz : Element{ //anderer Code... public override string Serialisieren(){ return "N" + this.Inhalt; } }
In der Speichern-Methode kannst du dann einfach die Element-Liste durchlaufen und jeweils die Serialisieren-Methode aufrufen. Das zurück gegebene schriebst du dann einfach jeweils auf eine neue Zeile.
Beim wieder auslesen kannst du nun Zeile für Zeile einlesen und kannst anhand des ersten Zeichens entscheiden, wie du den nachfolgenden Inhalt zu verarbeiten hast.
Das mit dem ersten Zeichen einer Zeile ist nur eine von unzähligen Möglichkeiten. Du könntest auch komplexere Formate wie XML oder JSON dafür benutzen oder das ganze auch binär abspeichern. Aber ich denke das Beispiel zeigt ganz gut wie so etwas funktionieren kann.
PS: Noch 2 kleine Tipps:
- Passe bei der Benennung auf. Deine Liste "Element" impliziert dass es nur ein einzelnes ist. Listen benennt man daher meistens in der Mehrzahl, hier also Elemente bzw. Elements auf Englisch.
- Wenn du mit Streams arbeitest, empfiehlt es sich using-Blöcke einzusetzen. Diese schließen den Stream ggf. automatisch, auch im Fehlerfall. Ein Aufruf von Close ist dann nicht mehr nötig.
Mehr eine Diskussionsfrage ist hier, ob man lieber alles auf englisch benennen sollte. Ich habe es mir recht schnell so angewöhnt, einfach weil auch alles andere auf Englisch ist. Aber auf Deutsch funktioniert das natürlich trotzdem.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 29. November 2016 07:18
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 20. Dezember 2016 09:47
-
Hallo Luca,
wie genau hast du denn deine Klassen Notizen, Termine und ToDos implementiert? Mich würde vor allem interessieren, für was der zweite Parameter des Konstruktors nützlich ist.
Deine Methode btnName_Click dient sicherlich dem hinzufügen eines neuen Elements. Aber warum wird zur Liste jeweils eine Notiz, ein Termin und ein ToDo mit dem gleichen Titel(?) hinzugefügt?
In deiner Methode Schreiben müsste eigentlich eine InvalidCastException ausgelöst werden, sobald du bspw. versuchst, einen Termin in eine Notiz zu casten. Und ich verstehe nicht, warum du dafür eine foreach-Schleife verwendest. Letztendlich würdest du in den entsprechenden TextBoxen nur den Inhalt des letzten Elements zu sehen bekommen.
Ich hoffe, das Ziel deiner Anwendung verstanden zu haben. Bei mir sieht es jetzt wie folgt aus:
Deinen Code habe ich etwas umgestaltet:
interface IElement { string Beschreibung {get; set; } } class Notiz : IElement { public string Beschreibung {get; set; } public override string ToString () { return "Notiz: " + Beschreibung: } } class Termin : IElement { //... } class ToDo : IElement { //... } public partial class Form1 : Form { List<IElement> elemente = new List<IElement>(); public Form1() { InitializeComponent(); } private void btn_Hinzufuegen_Click (object sender, EventArgs e) { IElement neuesElement; if (radioButton_Notiz.Checked) { neuesElement = new Notiz(); } else if (radioButton_Termin.Checked) { neuesElement = new Termin() } else { neuesElement = new ToDo(); } neuesElement.Beschreibung = tb_neuesElement.Text; elemente.Add(neuesElement); listBox.Items.Add(neuesElement); tb_neuesElement.Text = String.Empty; } private void btn_Loeschen_Click (object sender, EventArgs e) { listBox.Items.Remove(listBox.SelectedItem); } private void ListBox_SelectedIndexChanged (object sender, EventArgs e) { SchreibeInTextBoxen(); } private void SchreibeInTextBoxen () { IElement ausgewaehltesElement = (IElement) listBox.SelectedItem; tb_Beschreibung.Text = ausgewaehltesElement?.Beschreibung; } }
Entschuldige bitte eventuelle Fehler - ich musste das gerade per Hand abtippen.
Als letztes noch der Hinweis, dass du Datenbindung nutzen kannst, um deine Liste Elemente an ListBox.Items zu binden. Dann musst du nicht jede Aktion sowohl für Liste als auch die Auflistung der ListBox durchführen.
Beste Grüße- Bearbeitet Flogex Freitag, 25. November 2016 15:58
- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 29. November 2016 07:25
-
Hallo und Danke für die super ausführliche Antwort.
Das mit der Klasse Element die an die 3 Unterklassen vererbt stimmt genau.
Ich habe mir meinen eigenen Code nun nochmal angesehen und bin nun auch wieder auf einige kleine Fehlerchen gestoßen.
Das mit der Serialisieren Methode werde ich auf jeden Fall versuchen, da wäre ich niemals auf die Idee gekommen.
Ich werde die Klasse überarbeiten und dann eine neue Version hier hochladen.
Danke vielmals!
-
Hierfür kannst du eine abstrakte Methode benutzen, die du in den 3 Ableitungen überschreibst:
//nur wenn die Klasse abstract ist, kann auch eine Methode abstract sein public abstract class Element{ //anderer Code... public abstract string Serialisieren(); } public class Notitz : Element{ //anderer Code... public override string Serialisieren(){ return "N" + this.Inhalt; } }
In der Speichern-Methode kannst du dann einfach die Element-Liste durchlaufen und jeweils die Serialisieren-Methode aufrufen. Das zurück gegebene schriebst du dann einfach jeweils auf eine neue Zeile.
Beim wieder auslesen kannst du nun Zeile für Zeile einlesen und kannst anhand des ersten Zeichens entscheiden, wie du den nachfolgenden Inhalt zu verarbeiten hast.
Das mit dem ersten Zeichen einer Zeile ist nur eine von unzähligen Möglichkeiten. Du könntest auch komplexere Formate wie XML oder JSON dafür benutzen oder das ganze auch binär abspeichern. Aber ich denke das Beispiel zeigt ganz gut wie so etwas funktionieren kann.
PS: Noch 2 kleine Tipps:
- Passe bei der Benennung auf. Deine Liste "Element" impliziert dass es nur ein einzelnes ist. Listen benennt man daher meistens in der Mehrzahl, hier also Elemente bzw. Elements auf Englisch.
- Wenn du mit Streams arbeitest, empfiehlt es sich using-Blöcke einzusetzen. Diese schließen den Stream ggf. automatisch, auch im Fehlerfall. Ein Aufruf von Close ist dann nicht mehr nötig.
Mehr eine Diskussionsfrage ist hier, ob man lieber alles auf englisch benennen sollte. Ich habe es mir recht schnell so angewöhnt, einfach weil auch alles andere auf Englisch ist. Aber auf Deutsch funktioniert das natürlich trotzdem.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippetsusing System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; using System.Xml.Serialization; namespace Organisierer { public partial class Form1 : Form { List<Elemente> Elements = new List<Elemente>(); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void btnName_Click(object sender, EventArgs e) { Elements.Add(new Notizen(tbNeu.Text, "Notizen")); Elements.Add(new Termine(tbNeu.Text, "Termin")); Elements.Add(new ToDos(tbNeu.Text, "ToDo")); foreach(Elemente ele in Elements) { Console.WriteLine(ele); } lbBox.Items.Add(tbNeu.Text); tbNeu.Text = ""; } private void btnLöschen_Click(object sender, EventArgs e) { Console.WriteLine(lbBox.SelectedItem.ToString() + " gelöscht."); lbBox.Items.Remove(lbBox.SelectedItem); tbNotizen.Text = ""; tbTermine.Text = ""; tbToDos.Text = ""; } private void btnLaden_Click(object sender, EventArgs e) { try { string curr = lbBox.SelectedItem.ToString(); Console.WriteLine("Momentan ist Element:{0} ausgewählt", curr); } catch (Exception exc) { Console.WriteLine(exc.Message); } } private void lbBox_SelectedIndexChanged(object sender, EventArgs e) { try { string curr = lbBox.SelectedItem.ToString(); Console.WriteLine("Momentan ist Element:{0} ausgewählt", curr); } catch (Exception exc) { Console.WriteLine(exc.Message); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Speichern(); } private void btnSpeichern_Click(object sender, EventArgs e) { Speichern(); } public void Speichern() { using (StreamWriter sw = new StreamWriter("Text.txt")) { foreach (Elemente ele in Elements) { sw.WriteLine(ele.Serialisieren()); } } } } }
Soweit so gut. Ich habe leider nur begrenzte Mittel, sonst würde ich gerne mal die Datei selbst hochladen.
Wie kann ich die geschriebenen Elemente nun wieder einlesen? Vorallem wie bekomme ich jeweils die Buchstaben wieder entfernt? Ich habe nun für:
Notizen N
Termine T
ToDos O
Danke im Voraus !
-
Hallo Luca,
public static List<Element> ReadElementsFromFile (string path) { List<Element> elements = new List<Element>(); using (StreamReader sr = new StreamReader(path)) { while (!sr.EndOfStream) { string identifierPlusContent = sr.ReadLine(); Element newElement = StaticElementFactory.GetElementFromString(identifierPlusContent); elements.Add(newElement); } } return elements; }
class StaticElementFactory { public static Element GetElementFromString(string identifierPlusContent) { Element newElement; char identifier = identifierPlusContent.First(); switch (identifier) { case 'N': newElement = new Notiz(); break; case 'T': newElement = new Termin(); break; case 'O': newElement = new ToDo(); break; default: newElement = null; break; } newElement.Content = identifierPlusContent.Remove(0, 1); return newElement; } }
Beste Grüße- Als Antwort vorgeschlagen Dimitar DenkovMicrosoft contingent staff, Administrator Freitag, 9. Dezember 2016 10:48
- Als Antwort markiert Dimitar DenkovMicrosoft contingent staff, Administrator Dienstag, 20. Dezember 2016 09:47