Entwickler-Ecke

WinForms - ArrayList in Listbox kopieren


coolace - So 17.08.08 16:29
Titel: ArrayList in Listbox kopieren
Hy,

ich versuch mir eine sauber Programmierung anzugewöhnen um mögliche Fehler zu vermeiden
und besser zu werden. Ich habe folgendes Problem, ich less über eine Textbox einen Namen
ein und über einen Datetimepicker das Datum ein. Hab mir dafür eine Klasse daten erzeugt mit
den Property, die klasse speichert 2 strings gebname und gebdatum und hat eine Methode tostring()
wo beide Daten mit Leerstring zusasmmenfasst. Z.B. Hans 27.01.1980

Durch ein Button wird ein Eintrag erzeugt, und hier kommt die InvalidOperationExcextion.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
public void Eintragadd(string sd, DateTime date)
        {
            daten d = new daten();
            d.Gebname = sd;
            d.Gebdatum = date.ToShortDateString();
            liste.Add(d);
            //liste.Sort(new datecompare());
            foreach (daten temp in liste)
            {
                liste.Add(temp.ToString());
            }
      }


Was ist hier falsch ?

LG

Coolace

Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt


Christian S. - So 17.08.08 16:53

Hallo!

Du benutzt in der Schleife sowohl liste als die Liste, durch die Du iterierst als auch als Liste, in die Du Elemente einfügst. Das klappt nicht.

Noch ein paar Hinweise, die nicht mit dem unmittelbaren Problem zusammen hängen:
- Typen immer groß schreiben. Also daten -> Daten
- ArrayList ist veraltet, also lieber mit List<Daten> arbeiten. Dadurch entällt auch etwaiges rumgecaste. Andere Möglichkeit: BindingList<Daten> (siehe unten)
- Wenn Du ein Datum speichern willst, dann speichere es als DateTime und nicht als String
- Abkürzungen wie "Gebname" sparen in moderenen IDEs mit Codevervollständigung fast keinerlei Tipparbeit, vermindern aber die Lesbarkeit sehr.

Nun noch zur BindingList<Daten>. Zu benutzen praktisch so wie eine ArrayList. Vorteil: Sie stellt Ereignisse bereit, damit Komponenten wie die Listbox mitbekommen, wenn Einträge hinzugefügt oder entfernt werden. Dann aktualisieren die sich von selber.

Benutzung wäre so:
- privates Feld: BindingList<Daten> datenListe;
- im Load-Ereignis (oder Konstruktor) der Form dann das hier:

C#-Quelltext
1:
2:
datenListe = new BindingList<Daten>();
dieListbox.DataSource = datenListe;


Wenn Du nun der datenListe ein Item hinzufügst, dann bekommt die Listbox das automatisch mit. Das ist viel eleganter, als sich selber um die Anzeige kümmern zu müssen. :-)

Grüße
Christian

P.S.: Es gibt auch noch die Möglichkeit, dass die Listbox mitbekommt, wenn Du ein Geburtsdatum oder einen Namen änderst, nachdem das Item schon eingefügt wurde. Aber dazu späer mehr ;-)


coolace - Mo 18.08.08 11:48

vielen Dank für deine Hilfe, hab das Problem gesehen dank deinem Hinweis.
Meine Listbox heißt lbox und bin dann beim tippen mit lbox und liste durcheinander
gekommen so das es aussieht als würde er im Kreis kopieren.
in die Databinding geschichten werd ich mich demnächst mal einarbeiten.


coolace - Mo 18.08.08 21:53

ich habe das getestet und einen merkwürdigen Fehler festgestellt, sobald ich eine leere Datei habe und das Programm starte kann ich beliebig viele Personen mit geburtsdatum einfügen. Sobald ich aber die Datei schließe, die Einträge speicher und wieder öffne und zu diesen Einträgen einen Hinzufüge beokmm ich eine InvalidCastException gleich bei der foreachschleife foreach (daten temp in liste) <--hier

Ich hab auch auf eure Empfehlung hin den String auf Datetime umgestellt in der Klasse.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
public void Eintragadd(string sd, DateTime date)
        {
            daten d = new daten();
            d.Gebname = sd;
            d.Gebdatum = date;
            liste.Add(d);
            liste.TrimtoSize();
            lbox.items.clear();
            foreach (daten temp in liste)
            {
                lbox.Add(temp.ToString());
            }
      }


Er sagt was von Das Objekt des Typ System.string kann nicht in Typ WindowsApplicaton11.daten umgewandelt werden
LG

Coolace

Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt


Christian S. - Mo 18.08.08 22:15

Öhm, lies doch mal bitte Deinen Beitrag durch und überlege, ob Dir jemand helfen kann, der Dein Programm nicht kennt.

Wie speicherst Du die Daten?
Wie lädst Du die Daten?

Ich kann Dir auch nicht mehr sagen, als die Exception schon sagt: Da ist ein String in der Liste und Du versuchst, in als Daten zu verwenden. Alles weitere ist ein Fall für die Astrologie :nixweiss:

//edit: Und jetzt benutze doch endlich List<Daten>! Dann kann sowas gar nicht passieren! ArrayList ist ALT und TOT für neue Projekte!


coolace - Mo 18.08.08 22:22

Sorry,

hier mein komplettes Programm

Hauptprogramm

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
public partial class Form1 : Form
    {
        
      string n,datum;
      Int32 startindex;
      ArrayList liste = new ArrayList();
        public Form1()
        {
            InitializeComponent();
            try

            {
                Dateierstellung();
                lbldate.Text = DateTime.Now.ToLongDateString();
                //fügt die Geburtstagsinfos ein
                liste.AddRange(File.ReadAllLines(@"E:\Butler\geburtstag.txt"));
                Geburtstagskind();
            }
            catch (Exception)
            {
                
                MessageBox.Show(this"Eine der Dateien existiert nicht""Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnexit_Click(object sender, EventArgs e)
        {

            Int32 i = 0;
            string[] speicher = new string[liste.Capacity];
            foreach (daten temp in liste)
            {
                speicher[i] = temp.ToString();
                i++;
            }
            File.WriteAllLines(@"E:\Butler\geburtstag.txt",speicher);
            
            this.Close();
        }

        private void btnadd_Click(object sender, EventArgs e)
        {
            geburtstag geb = new geburtstag();
            geb.Show(this);
        }
        
        //Anlegen der beiden Dateien
        private void Dateierstellung()
        {
            

            if (File.Exists(@"E:\Butler\geburtstag.txt")) { }
            else
            {
                File.Create(@"E:\Butler\geburtstag.txt");
            }

        }

        private void Form1_Load(object sender, EventArgs e)
        {
           
        }

        //Hinzufügen des Datums vom Auswahlfenster
        public void Eintragadd(string sd, DateTime date)
        {
            daten d = new daten();
            d.Gebname = sd;
            d.Gebdatum = date;
            liste.Add(d);
            //liste.Sort(new datecompare());

            liste.TrimToSize();
            lbox.Items.Clear();
            foreach (daten temp in liste)
            {
                lbox.Items.Add(temp.ToString());
            }
      }

        //Auswahl der richtigen Person und anzeige in der Listbox
        public void Geburtstagskind()
        {
            
            foreach (string temps in liste)
            {
                lbox.Items.Add(temps);
            }

        }

        private void btndel_Click(object sender, EventArgs e)
        {
            try
            {
                lbox.Items.RemoveAt(lbox.SelectedIndex);
            }
            catch (IndexOutOfRangeException)
            {

                MessageBox.Show(this"Sie müssen einen Eintrag auswählen""Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

Klasse

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
class daten
    {
        string gebname;
        DateTime gebdatum;

        public string Gebname
        {
            get { return gebname; }
            set { gebname = value; }
        }

        public DateTime Gebdatum
        {
            get { return gebdatum; }
            set { gebdatum = value; }
        }

        public override string ToString()
        {
           string s = gebdatum.ToShortDateString() + " " + gebname;
           return s;
        }
}
          
        
        }


Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt


Christian S. - Mo 18.08.08 22:31

Jo. Du liest die Liste hiermit ein:


C#-Quelltext
1:
liste.AddRange(File.ReadAllLines(@"E:\Butler\geburtstag.txt"));                    

Das sind alles Strings. Und die versuchst Du als Daten zu verwenden. Kann nicht klappen.



Du musst die Daten so speichern, dass Du sie hinterher wieder in ein Daten-Objekt einlesen kannst. Du brauchst also im Prinzip eine Umkehrung der ToString-Methode, die Du auf jeden Zeile der Datei anwendest, damit daraus wieder Daten-Objekt wird.

Viel besser wäre es wohl, das Speichern und Laden dem XmlSerializer zu überlassen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
List<Daten> liste = new List<Daten>();

liste.Add(...);
liste.Add(...);

/* ... */

XmlSerializer xs = new XmlSerializer(typeOf(List<Daten>));

//Speichern
using(FileStream fs = File.Create(@"E:\Butler\geburtstag.xml"))
  xs.Serialize(liste, fs); //evtl. andersrum, weiß ich aus dem Gedächtnis nicht

//Laden
using(FileStream fs = File.OpenRead(@"E:\Butler\geburtstag.xml"))
  liste = (List<Daten>) xs.Deserialize(fs);



Und nun noch eine Bitte: Du hast sicherlich bemerkt, dass unter jedem Deiner Postings ein Hinweis steht, dass ich C#-Tags hinzugefügt habe. Mache das bitte demnächst selber. Hier [http://www.c-sharp-forum.de/help_schreiben_bbcodes_source.html&sub=,19,27,32] die Erklärung dazu.


coolace - Di 19.08.08 08:23

Hy,

erstmal vielen vielen Dank für deine Mühe mir weiterzuhelfen und entschuldige wegen den C# Tags, hab ich bei den Boardregelen überlesen und daher nicht gemacht. Werd mich bessern aber danke für den Hinweis, macht den code gleich übersichtlicher.

Jetzt kapier ich auch warum der Compiler so ein Problem damit hat. XML wär eine feine Sache, muss ich dann über meine Klasse noch wie beim Binärformat [Serialisable] davor machen oder reicht das was du hingeschrieben hast ?

Gruß

Coolace


Christian S. - Di 19.08.08 10:36

Wenn Du die Serialisierung nicht irgendwie beeinflussen willst (also den Knoten andere Namen geben als die Properties haben oder eine Property als Attribut anstatt als Knoten serialisieren), dann sollte der Serializer eigentlich mit den allermeisten Klassen klar kommen, ohne dass man mehr machen muss; als ich geschrieben habe. Es werden halt alle Properties serialisiert, die einen Getter oder Setter haben.


coolace - Di 19.08.08 21:29

sorry aber ich hab das so hinzugefügt wie du es beschrieben hast und als es an das Speichern ging
sagt er InvalidOperationException (beim Generieren des XML Dokuments ist ein Fehler aufgetreten.

Hab ich irgendwas übersehen oder muss ich irgendwas an meinem Quellcode ändern


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
 private void btnexit_Click(object sender, EventArgs e)
        {

            using(FileStream fs = File.Create(@"E:\Butler\geburtstag.xml"))
            xs.Serialize(ArrayList, fs); 
           this.Close();
        }

und

InitializeComponent();
            try

            {
                Dateierstellung();
                lbldate.Text = DateTime.Now.ToLongDateString();
                //fügt die Geburtstagsinfos ein
                using(FileStream fs = File.OpenRead(@"E:\Butler\geburtstag.xml"))
                liste = (ArrayList) xs.Deserialize(fs);
                Geburtstagskind();
            }
            catch (Exception)
            {
                
                MessageBox.Show(this"Eine der Dateien existiert nicht""Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

Dies hab ich so übernommen, das einzige was ich geändert habe ist von Liste<Daten> auf Arraylist.

LG

CoolAce


Christian S. - Di 19.08.08 21:38

user profile iconcoolace hat folgendes geschrieben:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
 private void btnexit_Click(object sender, EventArgs e)
        {

            using(FileStream fs = File.Create(@"E:\Butler\geburtstag.xml"))
            xs.Serialize(ArrayList, fs); 
           this.Close();
        }

Das kompiliert? ArrayList ist doch eine Klasse und keine Instanz.


user profile iconcoolace hat folgendes geschrieben:
das einzige was ich geändert habe ist von Liste<Daten> auf Arraylist.
Du bist echt beratungs-resistent. Jetzt mal konkret: Warum benutzt Du die ArrayList anstatt der List<T>-Klasse?


coolace - Di 19.08.08 22:31

hy,

hab ich übersehen und geändert , großes Sorry. Hab jetzt eine Parametrisierte Liste.
Trotzdem geht es irgendwie nicht mit

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
private void btnexit_Click(object sender, EventArgs e)
        {

            using(FileStream fs = File.Create(@"E:\Butler\geburtstag.xml"))
            xs.Serialize(fs, liste); 
           this.Close();
        }

LG

Coolace


Christian S. - Di 19.08.08 22:38

Hm. :gruebel: Zeig mal bitte Deine Daten-Klasse.

//edit: Ist Deine Klasse public? Das muss sie sein für den XMLSerializer!


coolace - Mi 20.08.08 20:27

Hy,

Sie war nicht public, hab Sie jetzt geändert aber er bringt immer noch den selben Fehler, hier nur die daten Klasse

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
public class daten
    {
        string gebname;
        DateTime gebdatum;

        public string Gebname
        {
            get { return gebname; }
            set { gebname = value; }
        }

        public DateTime Gebdatum
        {
            get { return gebdatum; }
            set { gebdatum = value; }
        }

        public override string ToString()
        {
           string s = gebdatum.ToShortDateString() + " " + gebname;
           return s;
        }

        public string Alter()
        {
            string age = Convert.ToString(DateTime.Now.Year - gebdatum.Year);
            return age;
        }

    }
}


LG

Coolace


Christian S. - Mi 20.08.08 20:30

Häng mal bitte das gesamte Projekt an, ich sehe da im Moment keinen Fehler :nixweiss:


coolace - Mi 20.08.08 20:33

werden .zip Dateien freigeschalten oder soll ich es extern hochladen und den Link posten ?


Christian S. - Mi 20.08.08 20:37

zip-Dateien bis 2MB sind erlaubt.
(die Ordner "bin" und "obj" brauchst Du nicht mit einpacken)


coolace - Mi 20.08.08 20:39

Hy,

habs mal schnell auf nen hoster gelegt.

http://www.materialordner.de/aBJ1wl9u0tAZtGgfOdPDoxyxdbKF2PG.html

Vielen Dank

Gruß

Coolace


Christian S. - Mi 20.08.08 20:44

Im Konstruktor des XMLSerializers verwendest Du noch ArrayList. Das passt dann nicht mit der List<daten> zusammen.


C#-Quelltext
1:
XmlSerializer xs = new XmlSerializer(typeof(ArrayList)); //<-- muss List<daten> sein                    


coolace - Mi 20.08.08 20:51

das geht, isch werd verrückt, das geht :-)
vielen vielen Dank für die Geduld. Nur noch ein kleinen Tipp bräuchte ich
ich will die Liste nach Datum sortieren, in der Arrayklasse brauch ich ja eine
IComparer Klasse dafür, brauch ich das hier auch ?


Christian S. - Mi 20.08.08 20:58

Du hast verschiedene Möglichkeiten die Sort-Methode der Liste zu verwenden.
- Du kannst in der daten-Klasse das IComparable-Interface implementieren und Sort ohne Parameter aufrufen
- Oder Du kannst eine Comparison<daten>-Methode implementieren, z.B. mit einer anonymen Methode:

C#-Quelltext
1:
 liste.Sort(delegate(daten x, daten y) { return x.Gebdatum.CompareTo(y.Gebdatum); });                    


Wenn Du C# 3.5 benutzt, kannst Du auch einen Lambda-Ausdruck bentuzen:

C#-Quelltext
1:
liste.Sort((x, y) =>  x.Gebdatum.CompareTo(y.Gebdatum));                    


lothi - Mi 20.08.08 21:02

Hallo coolace

Es gibt da ein List<T>.Sort

edit:
zu spät
Gruss Lothi


coolace - Do 21.08.08 21:34

Hy,

dankeschön, das letzte Problem was ich noch hab ist das ich den Markierten Eintrag zwar von der listbox mit
listbox.Items.RemoveAt(listbox.SelectedIndex) löschen kann aber wenn ich den selben index mittels
liste.RemovAt(listbox.SelectedIndex) übergebe geht es nicht weil er einen negativen Index -1 zurückbekommt.
Wie bekomm ich das hin das er das aus meiner liste sauber rauslöscht ?


Christian S. - Do 21.08.08 21:38

Eigentlich gilt "Neue Frage -> Neuer Thread".

Aber wenn Du den selektierten Eintrag aus einer Listbox löschst, ist nix mehr selektiert, also ist danach SelectedIndex = -1.

Lösche erst aus der liste, dann aus der Listbox.


coolace - Do 21.08.08 21:48

ok vielen Dank

LG

Coolace